<?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[ Beginner Developers - 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[ Beginner Developers - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 25 Aug 2026 07:25:06 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/beginners/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Basic Discord Storytelling, Chat, and Mental Wellness Bot with Python ]]>
                </title>
                <description>
                    <![CDATA[ Discord bots can look surprisingly complicated when you see them in action. A bot can respond to messages, tell stories, remember parts of conversations, and stay online around the clock. When I first ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-basic-discord-bot-with-python/</link>
                <guid isPermaLink="false">6a7f44fc58366ecdaf016624</guid>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bot ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python 3 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ techblog ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Eva J Patel ]]>
                </dc:creator>
                <pubDate>Fri, 14 Aug 2026 16:40:28 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/6a444c51-d332-4915-aa1f-326b57b17472.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Discord bots can look surprisingly complicated when you see them in action. A bot can respond to messages, tell stories, remember parts of conversations, and stay online around the clock.</p>
<p>When I first started looking into how they worked, I assumed there had to be a huge amount of complicated code behind all of it.</p>
<p>But the basic idea is actually pretty simple.</p>
<p>At its core, a Discord bot is just a Python program that connects to Discord, waits for something to happen, and then decides how to respond. Once you understand that basic idea, you can start adding features one at a time and turn a simple bot into something much more interesting.</p>
<p>In this tutorial, we'll start with a very small bot and gradually build it into something more capable. Along the way, you'll learn about Discord commands, events, asynchronous Python, user state, environment variables, and basic deployment.</p>
<p>One quick disclaimer before we start: the mental-wellness feature that we'll be integrating in this bot in this project is <strong>not therapy</strong>, and the bot is not a therapist or medical professional. It should only provide general supportive suggestions and encourage users to reach out to a trusted person when appropriate.</p>
<p>With that out of the way, let's get coding!</p>
<h3 id="heading-what-well-cover">What We'll Cover:</h3>
<ul>
<li><p><a href="#heading-what-were-building">What We're Building</a></p>
</li>
<li><p><a href="#heading-what-you-need">What You Need</a></p>
</li>
<li><p><a href="#heading-create-the-discord-bot">Create the Discord Bot</a></p>
</li>
<li><p><a href="#heading-give-the-bot-permission-to-read-messages">Give the Bot Permission to Read Messages</a></p>
</li>
<li><p><a href="#heading-create-the-project">Create the Project</a></p>
</li>
<li><p><a href="#heading-create-a-virtual-environment">Create a Virtual Environment</a></p>
</li>
<li><p><a href="#heading-install-discordpy">Install discord.py</a></p>
</li>
<li><p><a href="#heading-create-your-first-bot">Create Your First Bot</a></p>
<ul>
<li><p><a href="#heading-importing-our-libraries">Importing Our Libraries</a></p>
</li>
<li><p><a href="#heading-loading-the-token">Loading the Token</a></p>
</li>
<li><p><a href="#heading-understanding-intents">Understanding Intents</a></p>
</li>
<li><p><a href="#heading-what-is-ctx">What Isctx?</a></p>
</li>
<li><p><a href="#heading-why-does-everything-say-async-and-await">Why Does Everything Sayasyncandawait?</a></p>
</li>
<li><p><a href="#heading-run-the-bot">Run the Bot</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-build-the-storytelling-system">Build the Storytelling System</a></p>
<ul>
<li><p><a href="#heading-lets-make-the-story-remember-the-user">Let's Make the Story Remember the User</a></p>
</li>
<li><p><a href="#heading-add-a-story-choice">Add a Story Choice</a></p>
</li>
<li><p><a href="#heading-add-a-casual-chat-command">Add a Casual Chat Command</a></p>
</li>
<li><p><a href="#heading-add-a-mental-wellness-support-feature">Add a Mental-Wellness Support Feature</a></p>
</li>
<li><p><a href="#heading-add-a-help-command">Add a Help Command</a></p>
</li>
<li><p><a href="#heading-improve-error-handling">Improve Error Handling</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-put-everything-together">Put Everything Together</a></p>
</li>
<li><p><a href="#heading-our-bot-doesnt-actually-remember-anything">Our Bot Doesn't Actually Remember Anything</a></p>
<ul>
<li><p><a href="#heading-create-the-database">Create the Database</a></p>
</li>
<li><p><a href="#heading-save-a-users-story">Save a User's Story</a></p>
</li>
<li><p><a href="#heading-get-the-story-back">Get the Story Back</a></p>
</li>
<li><p><a href="#heading-put-it-into-a-command">Put It Into a Command</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-adding-real-ai-chat">Adding Real AI Chat</a></p>
<ul>
<li><p><a href="#heading-install-the-hugging-face-library">Install the Hugging Face Library</a></p>
</li>
<li><p><a href="#heading-create-the-hugging-face-client">Create the Hugging Face Client</a></p>
</li>
<li><p><a href="#heading-connect-the-ai-model-to-the-bot">Connect the AI Model to the Bot</a></p>
</li>
<li><p><a href="#heading-handle-ai-errors">Handle AI Errors</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-how-do-we-keep-the-bot-online">How Do We Keep the Bot Online?</a></p>
<ul>
<li><p><a href="#heading-option-1-run-it-on-your-computer">Option 1: Run It on Your Computer</a></p>
</li>
<li><p><a href="#heading-option-2-host-it-on-a-server">Option 2: Host It on a Server</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-what-forever-actually-means">What "Forever" Actually Means</a></p>
</li>
<li><p><a href="#heading-dont-try-to-keep-it-awake-with-random-tricks">Don't Try to "Keep It Awake" With Random Tricks</a></p>
</li>
<li><p><a href="#heading-additional-features-and-where-to-go-next">Additional Features and Where to Go Next</a></p>
</li>
<li><p><a href="#heading-test-everything-locally-first">Test Everything Locally First</a></p>
</li>
<li><p><a href="#heading-deploying-the-bot">Deploying the Bot</a></p>
<ul>
<li><a href="#heading-the-start-command">The Start Command</a></li>
</ul>
</li>
<li><p><a href="#heading-remember-keep-your-secrets-secret">Remember: Keep Your Secrets Secret</a></p>
</li>
<li><p><a href="#heading-what-you-learned">What You Learned</a></p>
</li>
<li><p><a href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-what-were-building">What We're Building</h2>
<p>Our finished bot will have several commands:</p>
<pre><code class="language-text">!hello
!story
!chat hello!
!support I'm having a stressful day
!help
</code></pre>
<p>For example:</p>
<pre><code class="language-text">User:
!story

Bot:
You wake up inside an abandoned library.

There are three doors in front of you:

1. A red wooden door
2. A metal door covered in strange symbols
3. A staircase leading underground

Which one do you choose?
</code></pre>
<p>The user can then continue the story.</p>
<p>For chat:</p>
<pre><code class="language-text">User:
!chat What's a good way to learn Python?

Bot:
Try building small projects instead of only reading tutorials.
A Discord bot is actually a pretty fun project to start with.
</code></pre>
<p>And for mental-wellness support:</p>
<pre><code class="language-text">User:
!support I'm really stressed about school.

Bot:
That sounds like a lot to deal with. You could try breaking
the work into one small task at a time and taking a short
break between tasks.

I'm a bot, not a therapist, so if you need personal support,
consider talking with someone you trust.
</code></pre>
<p>The goal isn't to make a magical robot therapist. It's to build a useful bot while learning how Discord APIs, Python functions, events, asynchronous programming, and basic conversational logic fit together.</p>
<h2 id="heading-what-you-need">What You Need</h2>
<p>You only need a few things:</p>
<ul>
<li><p>Python (version 3.8+ is recommended)</p>
</li>
<li><p>A Discord account</p>
</li>
<li><p>A Discord server where you have permission to add a bot</p>
</li>
<li><p>A code editor (I personally prefer VS Code or PyCharm)</p>
</li>
<li><p>The <code>discord.py</code> library</p>
</li>
</ul>
<p>We'll also use Python's built-in <code>os</code> module for reading environment variables.</p>
<p>If you don't already have Python installed, install a current supported version of Python from the official Python website.</p>
<p>Then check that Python works:</p>
<pre><code class="language-bash">python --version
</code></pre>
<p>You should see something similar to:</p>
<pre><code class="language-text">Python 3.x.x
</code></pre>
<h2 id="heading-create-the-discord-bot">Create the Discord Bot</h2>
<p>Before Python can control Discord, we need to create a Discord application.</p>
<p>Go to the Discord Developer Portal: <a href="https://discord.com/developers/applications">https://discord.com/developers/applications</a></p>
<img src="https://cdn.hashnode.com/uploads/covers/6a581501af6af179dc1987d5/64a5aac8-fb53-42d0-9b91-fb6453b3eb1d.png" alt="Picture of the discord developer application page" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>This is what the page will look like, you might need to login with your discord email/username and password before you start.</p>
<p>Click on the "New Application" button on the top right and give your bot a name. For this tutorial, let's call ours <code>StoryBot</code>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a581501af6af179dc1987d5/84bd2f32-941c-4de2-8e6b-30ab22cc5ba1.png" alt="Picture of what it looks like when you click on the &quot;New Application&quot; button" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>The application is basically the home for your bot.</p>
<p>Discord's developer platform provides the tools needed to create and configure applications and bots.</p>
<p>Once you've created the application, open its <strong>Bot</strong> section and create the bot user. You can add your own icon picture and your own banner if you want to.</p>
<p>You will then go to the <strong>Token</strong> section and click on "Reset Token" to generate your token. Treat that token like a password. Do <strong>NOT</strong> put it directly into your Python source code.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a581501af6af179dc1987d5/7537d64e-706c-43b9-a574-746294703f6d.png" alt="Picture of what the Token part in the Bots section looks like" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Never do this:</p>
<pre><code class="language-python">bot.run("my-secret-token")
</code></pre>
<p>And definitely don't upload a token to GitHub or commit it to source control. Instead, we'll store it in an environment variable, which we'll talk about later.</p>
<h2 id="heading-give-the-bot-permission-to-read-messages">Give the Bot Permission to Read Messages</h2>
<p>Our bot needs to see the messages that contain commands.</p>
<p>Discord uses something called <strong>Gateway Intents</strong> to control which types of events a bot receives. The <code>discord.py</code> documentation explains that intents must be enabled both in your code and, for privileged intents, in the Discord Developer Portal.</p>
<p>In the Developer Portal, find:</p>
<pre><code class="language-text">Bot
→ Privileged Gateway Intents
</code></pre>
<p>Enable:</p>
<pre><code class="language-text">Message Content Intent
</code></pre>
<p>It should look somewhat like this:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a581501af6af179dc1987d5/0273691f-8723-41d3-8cfc-d59656dfb6e2.png" alt="What should the &quot;Message Content Intent&quot; section look like" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>We'll also enable it in Python, which we will talk about later in this article.</p>
<h2 id="heading-create-the-project">Create the Project</h2>
<p>Create a folder:</p>
<pre><code class="language-text">discord-story-bot/
</code></pre>
<p>Inside it, we'll eventually have:</p>
<pre><code class="language-text">discord-story-bot/
│
├── bot.py
├── requirements.txt
└── .env
</code></pre>
<p>The three important files are:</p>
<ul>
<li><p><code>bot.py</code>: our Python program</p>
</li>
<li><p><code>requirements.txt</code>: text file that contains the name of the packages our bot needs</p>
</li>
<li><p><code>.env</code>: our secret token during local development</p>
</li>
</ul>
<h2 id="heading-create-a-virtual-environment">Create a Virtual Environment</h2>
<p>Open your terminal inside the project folder.</p>
<p>Run:</p>
<pre><code class="language-bash">python -m venv venv
</code></pre>
<p>Then activate it.</p>
<p>On Windows:</p>
<pre><code class="language-bash">venv\Scripts\activate
</code></pre>
<p>On macOS/Linux:</p>
<pre><code class="language-bash">source venv/bin/activate
</code></pre>
<p>A virtual environment gives this project its own little Python bubble.</p>
<p>That means packages installed for this bot won't randomly interfere with packages used by another project.</p>
<h2 id="heading-install-discordpy">Install discord.py</h2>
<p>Now install the Discord library:</p>
<pre><code class="language-bash">pip install -U discord.py
</code></pre>
<p>The official <code>discord.py</code> documentation uses this installation approach for setting up the library.</p>
<p>We'll also install <code>python-dotenv</code>, which makes reading our local <code>.env</code> file easier:</p>
<pre><code class="language-bash">pip install python-dotenv
</code></pre>
<p>Then save the dependencies:</p>
<pre><code class="language-bash">pip freeze &gt; requirements.txt
</code></pre>
<p>Your <code>requirements.txt</code> should contain the packages needed by the project.</p>
<h2 id="heading-create-your-first-bot">Create Your First Bot</h2>
<p>Let's start small.</p>
<p>Open <code>bot.py</code>:</p>
<pre><code class="language-python">import os

import discord
from discord.ext import commands
from dotenv import load_dotenv


load_dotenv()

TOKEN = os.getenv("DISCORD_TOKEN")

intents = discord.Intents.default()
intents.message_content = True

bot = commands.Bot(
    command_prefix="!",
    intents=intents
)


@bot.event
async def on_ready():
    print(f"Logged in as {bot.user}")


@bot.command()
async def hello(ctx):
    await ctx.send("Hello! I'm online.")


bot.run(TOKEN)
</code></pre>
<p>That is already a functional Discord bot.</p>
<p>Let's break it apart piece by piece.</p>
<h3 id="heading-importing-our-libraries">Importing Our Libraries</h3>
<p>First:</p>
<pre><code class="language-python">import os
</code></pre>
<p><code>os</code> lets Python communicate with parts of the operating system.</p>
<p>We'll use it to read environment variables.</p>
<p>Next:</p>
<pre><code class="language-python">import discord
</code></pre>
<p>This imports <code>discord.py</code>.</p>
<p>Then:</p>
<pre><code class="language-python">from discord.ext import commands
</code></pre>
<p>The <code>commands</code> extension makes creating commands much easier.</p>
<p>Instead of manually checking every message for something like <code>!hello</code>, we can write:</p>
<pre><code class="language-python">@bot.command()
async def hello(ctx):
    await ctx.send("Hello!")
</code></pre>
<p>The <code>discord.py</code> command system is built around Python functions decorated as commands.</p>
<p>Finally:</p>
<pre><code class="language-python">from dotenv import load_dotenv
</code></pre>
<p>This lets us load values from our <code>.env</code> file.</p>
<h3 id="heading-loading-the-token">Loading the Token</h3>
<p>Our bot needs a token to connect our Python program to Discord. Think of the token as a password that allows our program to authenticate as the bot.</p>
<p>We don't want to put this secret directly into our Python code. Instead, we'll store it in an environment variable.</p>
<p>First, install <code>python-dotenv</code>:</p>
<pre><code class="language-bash">pip install python-dotenv
</code></pre>
<p>This package lets Python read values from a <code>.env</code> file.</p>
<p>Now create a new file called <code>.env</code> in the same folder as <code>bot.py</code>.</p>
<p>Inside <code>.env</code>, add:</p>
<pre><code class="language-text">DISCORD_TOKEN=YOUR_BOT_TOKEN_HERE
</code></pre>
<p>Replace <code>YOUR_BOT_TOKEN_HERE</code> with the token you copied from the Discord Developer Portal.</p>
<p>Your file should look something like this:</p>
<pre><code class="language-text">DISCORD_TOKEN=your_actual_token_here
</code></pre>
<p>Don't share this token with anyone or upload your <code>.env</code> file to GitHub. Your bot token should be treated like a password.</p>
<p>To make sure Git doesn't accidentally include the <code>.env</code> file in a repository, create a file called <code>.gitignore</code> in your project folder and add:</p>
<pre><code class="language-text">.env
venv/
__pycache__/
</code></pre>
<p>Now let's load the token in Python.</p>
<p>At the top of <code>bot.py</code>, add:</p>
<pre><code class="language-python">import os
from dotenv import load_dotenv
</code></pre>
<p>Then add:</p>
<pre><code class="language-python">load_dotenv()
</code></pre>
<p>This tells Python to look for the <code>.env</code> file and load the variables inside it.</p>
<p>Now we can get our Discord token:</p>
<pre><code class="language-python">TOKEN = os.getenv("DISCORD_TOKEN")
</code></pre>
<p><code>os.getenv()</code> looks for the environment variable named <code>"DISCORD_TOKEN"</code> and gives us its value.</p>
<p>We can also check that the token was actually found:</p>
<pre><code class="language-python">if not TOKEN:
    raise RuntimeError("DISCORD_TOKEN is not set.")
</code></pre>
<p>If Python can't find the token, the program stops and gives us a clear error message instead of failing later in a confusing way.</p>
<h3 id="heading-understanding-intents">Understanding Intents</h3>
<p>Remember how we talked about enabling message content readability in python? We are going to do that now.</p>
<p>Add:</p>
<pre><code class="language-python">intents = discord.Intents.default()
intents.message_content = True
</code></pre>
<p>The first line creates a set of Discord's default intents.</p>
<p>The second line tells Discord that our bot needs access to message content.</p>
<p>Now we need to give these intents to our bot when we create it:</p>
<pre><code class="language-python">bot = commands.Bot(
    command_prefix="!",
    intents=intents
)
</code></pre>
<p>The <code>command_prefix="!"</code> means our bot will recognize commands that begin with <code>!</code>.</p>
<p>For example:</p>
<pre><code class="language-text">!hello
</code></pre>
<p>The <code>intents=intents</code> part gives our bot the permissions we configured above.</p>
<p>There are two steps here because Discord needs to know that our bot is allowed to receive message content, while our Python program also needs to tell Discord that it wants to receive it.</p>
<p>Our basic setup should now look like this:</p>
<pre><code class="language-python">import os
import discord

from dotenv import load_dotenv
from discord.ext import commands

load_dotenv()

TOKEN = os.getenv("DISCORD_TOKEN")

if not TOKEN:
    raise RuntimeError("DISCORD_TOKEN is not set.")

intents = discord.Intents.default()
intents.message_content = True

bot = commands.Bot(
    command_prefix="!",
    intents=intents
)
</code></pre>
<p>Now our bot has its token safely loaded and <code>discord.py</code> knows which intents to request when it connects to Discord.</p>
<h3 id="heading-what-is-ctx">What Is <code>ctx</code>?</h3>
<p>This part can look weird when you're learning Discord bots:</p>
<pre><code class="language-python">async def hello(ctx):
</code></pre>
<p>What is <code>ctx</code>? <code>ctx</code> stands for <strong>context</strong>. It contains information about the command that was used.</p>
<p>For example, it can tell us:</p>
<ul>
<li><p>Who ran the command</p>
</li>
<li><p>Which server it came from</p>
</li>
<li><p>Which channel it came from</p>
</li>
<li><p>What message triggered it</p>
</li>
</ul>
<p>Then:</p>
<pre><code class="language-python">await ctx.send("Hello!")
</code></pre>
<p>means:</p>
<blockquote>
<p>"Send this message back to the place where the command was used."</p>
</blockquote>
<h3 id="heading-why-does-everything-say-async-and-await">Why Does Everything Say <code>async</code> and <code>await</code>?</h3>
<p>You might notice:</p>
<pre><code class="language-python">async def hello(ctx):
</code></pre>
<p>and:</p>
<pre><code class="language-python">await ctx.send(...)
</code></pre>
<p>Discord bots spend a lot of time waiting.</p>
<p>They wait for:</p>
<ul>
<li><p>Messages</p>
</li>
<li><p>Discord responses</p>
</li>
<li><p>API requests</p>
</li>
<li><p>Timers</p>
</li>
<li><p>Other events</p>
</li>
</ul>
<p>Python's asynchronous programming features allow the bot to wait for these operations without freezing everything else.</p>
<p>You don't need to become an async-programming expert before building your first bot.</p>
<p>For now, think of <code>await</code> as:</p>
<blockquote>
<p>"Pause this task until this operation finishes, while letting the bot handle other things."</p>
</blockquote>
<h3 id="heading-run-the-bot">Run the Bot</h3>
<p>Start it with:</p>
<pre><code class="language-bash">python bot.py
</code></pre>
<p>If everything works, your terminal should print something similar to:</p>
<pre><code class="language-text">Logged in as StoryBot
</code></pre>
<p>Now go to your Discord server and type <code>!hello</code>. Your bot should respond.</p>
<p>Congratulations! You've officially made a Discord bot.</p>
<p>Now let's make it interesting.</p>
<h2 id="heading-build-the-storytelling-system">Build the Storytelling System</h2>
<p>First, we're going to create an interactive storytelling command.</p>
<p>At the top of <code>bot.py</code>, add:</p>
<pre><code class="language-python">import random
</code></pre>
<p>Then create some story ingredients:</p>
<pre><code class="language-python">story_locations = [
    "an abandoned library",
    "a mysterious island",
    "a futuristic city",
    "a hidden underground laboratory",
    "a forest that never appears on maps"
]

story_items = [
    "a glowing key",
    "an ancient notebook",
    "a strange compass",
    "a locked metal box",
    "a mysterious photograph"
]

story_events = [
    "You hear footsteps behind you.",
    "The lights suddenly turn off.",
    "A hidden door opens nearby.",
    "Your phone starts displaying a message from an unknown sender.",
    "You notice that the room has changed."
]
</code></pre>
<p>Now create the command:</p>
<pre><code class="language-python">@bot.command()
async def story(ctx):
    location = random.choice(story_locations)
    item = random.choice(story_items)
    event = random.choice(story_events)

    story_text = (
        f"You wake up in {location}.\n\n"
        f"Next to you is {item}.\n\n"
        f"{event}\n\n"
        "What do you do?"
    )

    await ctx.send(story_text)
</code></pre>
<p>Now <code>!story</code> might produce:</p>
<pre><code class="language-text">You wake up in a futuristic city.

Next to you is an ancient notebook.

A hidden door opens nearby.

What do you do?
</code></pre>
<p>Run it again and you might get something completely different.</p>
<p>That's because of:</p>
<pre><code class="language-python">random.choice(...)
</code></pre>
<p>Python randomly picks one item from each list.</p>
<p>It's a simple technique, but suddenly your bot can generate hundreds of different combinations.</p>
<h3 id="heading-lets-make-the-story-remember-the-user">Let's Make the Story Remember the User</h3>
<p>Random stories are fun, but interactive stories are much better when the bot remembers what happened.</p>
<p>We can create a dictionary:</p>
<pre><code class="language-python">user_stories = {}
</code></pre>
<p>The dictionary will store story information for each user.</p>
<p>For example:</p>
<pre><code class="language-text">user ID → current story
</code></pre>
<p>Now let's modify the story command:</p>
<pre><code class="language-python">@bot.command()
async def story(ctx):
    user_id = ctx.author.id

    location = random.choice(story_locations)
    item = random.choice(story_items)
    event = random.choice(story_events)

    user_stories[user_id] = {
        "location": location,
        "item": item,
        "event": event
    }

    await ctx.send(
        f"You wake up in {location}.\n\n"
        f"Next to you is {item}.\n\n"
        f"{event}\n\n"
        "What do you do?"
    )
</code></pre>
<p>Now each user can have their own active story.</p>
<h3 id="heading-add-a-story-choice">Add a Story Choice</h3>
<p>Let's give users choices.</p>
<pre><code class="language-python">@bot.command()
async def choose(ctx, choice: str):
    user_id = ctx.author.id

    if user_id not in user_stories:
        await ctx.send("You don't have an active story. Try `!story` first.")
        return

    choice = choice.lower()

    if choice == "left":
        response = (
            "You head left and discover a room filled with old maps. "
            "One of them has your name written on it."
        )

    elif choice == "right":
        response = (
            "You head right and find a staircase leading toward "
            "a strange blue light."
        )

    else:
        response = "Try choosing `left` or `right`."

    await ctx.send(response)
</code></pre>
<p>Now users can type:</p>
<pre><code class="language-text">!choose left
</code></pre>
<p>or:</p>
<pre><code class="language-text">!choose right
</code></pre>
<p>Notice this:</p>
<pre><code class="language-python">async def choose(ctx, choice: str):
</code></pre>
<p>The <code>choice</code> parameter receives the text after the command.</p>
<p>So:</p>
<pre><code class="language-text">!choose left
</code></pre>
<p>becomes approximately:</p>
<pre><code class="language-python">choice = "left"
</code></pre>
<p>This is one of the reasons command frameworks are so convenient. A <strong>command framework</strong> is a set of tools that makes it easier to create and manage commands in a program. In our case, <code>discord.py</code> provides the command framework that lets us turn Python functions into Discord commands using decorators like <code>@bot.command()</code>.</p>
<p>Instead of manually checking every message to figure out whether someone typed <code>!choose</code>, <code>discord.py</code> handles that work for us. It recognizes the command, takes the user's arguments, and passes them to our function.</p>
<p>So when someone types:</p>
<pre><code class="language-text">!choose left
</code></pre>
<p><code>discord.py</code> knows that choose is the command, <code>"left"</code> is the argument, and that it should call our <code>choose()</code> function with that information.</p>
<h3 id="heading-add-a-casual-chat-command">Add a Casual Chat Command</h3>
<p>Now let's make the bot capable of basic conversation.</p>
<p>We could connect it to a large language model API, but you don't actually need AI to learn how a chat command works. We'll start with a simple keyword-based response system.</p>
<p>First, we'll create a dictionary containing some keywords and possible responses:</p>
<pre><code class="language-python">chat_responses = {
    "hello": [
        "Hey! What's up?",
        "Hello! How's your day going?",
        "Hi! What are you working on?"
    ],
    "python": [
        "Python is a great language for beginners because its syntax is pretty readable.",
        "If you're learning Python, try building something instead of only watching tutorials."
    ],
    "discord": [
        "Discord bots are a fun way to practice Python because you get instant feedback.",
        "Once you understand commands and events, you can build some surprisingly complex bots."
    ]
}

Think of `chat_responses` as a small collection of things our bot knows how to talk about. Each key, such as `"python"` or `"discord"`, represents a keyword the bot can look for. The value associated with each key is a list of possible responses.

We use a list instead of a single response so the bot doesn't give exactly the same answer every time. Later, we'll randomly choose one of these responses.

Now let's create the actual `!chat` command:

```python
@bot.command()
async def chat(ctx, *, message: str):
    text = message.lower()

    for keyword, responses in chat_responses.items():
        if keyword in text:
            await ctx.send(random.choice(responses))
            return

    await ctx.send(
        "I'm still learning how to respond to that. "
        "Try talking to me about Python or Discord!"
    )
</code></pre>
<p>There are a few things happening here, so let's break it down.</p>
<p>First, this part:</p>
<pre><code class="language-python">@bot.command()
async def chat(ctx, *, message: str):
</code></pre>
<p>turns the <code>chat()</code> function into a Discord command. The <code>*</code> is important because it tells <code>discord.py</code> to treat everything after the command as one argument.</p>
<p>For example, if someone types:</p>
<pre><code class="language-text">!chat I want to learn Python
</code></pre>
<p>the entire phrase after <code>!chat</code> becomes the value of <code>message</code>:</p>
<pre><code class="language-python">message = "I want to learn Python"
</code></pre>
<p>Next, we have:</p>
<pre><code class="language-python">text = message.lower()
</code></pre>
<p>This converts the message to lowercase. That means <code>Python</code>, <code>python</code>, and <code>PYTHON</code> will all become <code>python</code>. Without this, our keyword check could miss a match simply because the user capitalized a word differently.</p>
<p>Now we get to the loop:</p>
<pre><code class="language-python">for keyword, responses in chat_responses.items():
</code></pre>
<p><code>.items()</code> lets us go through both the keyword and its corresponding list of responses. During each loop, <code>keyword</code> contains something like <code>"python"</code>, while <code>responses</code> contains the list of responses associated with it.</p>
<p>Then we check:</p>
<pre><code class="language-python">if keyword in text:
</code></pre>
<p>This asks whether the current keyword appears anywhere in the user's message.</p>
<p>If the user writes:</p>
<pre><code class="language-text">!chat I want to learn Python
</code></pre>
<p>the lowercase version becomes:</p>
<pre><code class="language-text">i want to learn python
</code></pre>
<p>Since <code>"python"</code> appears inside that text, the condition is true.</p>
<p>The bot can then choose a random response:</p>
<pre><code class="language-python">await ctx.send(random.choice(responses))
</code></pre>
<p><code>random.choice()</code> picks one item from the response list, while <code>ctx.send()</code> sends that response back to the Discord channel.</p>
<p>Finally, we have:</p>
<pre><code class="language-python">return
</code></pre>
<p>This stops the function after a matching keyword is found. Without it, the loop would continue checking the other keywords even after the bot had already responded.</p>
<p>But what happens if none of the keywords match?</p>
<p>That's what this part handles:</p>
<pre><code class="language-python">await ctx.send(
    "I'm still learning how to respond to that. "
    "Try talking to me about Python or Discord!"
)
</code></pre>
<p>If the loop finishes without finding a keyword, the bot sends this fallback message instead.</p>
<p>For example:</p>
<pre><code class="language-text">!chat I like pizza
</code></pre>
<p>doesn't contain <code>"hello"</code>, <code>"python"</code>, or <code>"discord"</code>, so the bot doesn't have a specific response to use.</p>
<p>This gives us a simple way for the bot to have conversations without needing an AI model.</p>
<h3 id="heading-add-a-mental-wellness-support-feature">Add a Mental-Wellness Support Feature</h3>
<p>Now for the feature that needs a little more care.</p>
<p>Instead of calling this a "therapy command" internally, we'll call it:</p>
<pre><code class="language-text">!support
</code></pre>
<p>Quick additional disclaimer before we start...this is just a fun wellness script, not a real therapist!</p>
<p>Create:</p>
<pre><code class="language-python">support_responses = {
    "stress": [
        "That sounds like a lot to handle. Try breaking the situation into one small task at a time.",
        "When everything feels overwhelming, it can help to pause and focus on what needs attention right now."
    ],

    "school": [
        "School can pile up quickly. Consider choosing one assignment to work on first instead of trying to solve everything at once.",
        "If school stress is getting difficult to manage, talking with a trusted person can make things feel less like something you have to handle alone."
    ],

    "sad": [
        "I'm sorry you're having a difficult moment. Taking a short break, doing something calming, or talking with someone you trust may help.",
        "You don't have to solve everything immediately. Give yourself some time and consider reaching out to someone you trust."
    ]
}
</code></pre>
<p>Now create the command:</p>
<pre><code class="language-python">@bot.command()
async def support(ctx, *, message: str):
    text = message.lower()

    for keyword, responses in support_responses.items():
        if keyword in text:
            response = random.choice(responses)

            await ctx.send(
                f"{response}\n\n"
                "I'm a bot, not a therapist or medical professional. "
                "If you need personal support, consider talking with "
                "someone you trust."
            )
            return

    await ctx.send(
        "It sounds like something is bothering you. "
        "I can offer general wellness suggestions, but I'm not a therapist. "
        "If you need personal support, consider reaching out to someone you trust."
    )
</code></pre>
<p>Now someone can type:</p>
<pre><code class="language-text">!support I'm stressed about school
</code></pre>
<p>The bot sees the word:</p>
<pre><code class="language-text">school
</code></pre>
<p>and chooses one of the school-related responses.</p>
<p>This is deliberately simple.</p>
<p>For a real public bot, you'd want much more careful safety handling, testing, moderation, privacy protection, and escalation logic before allowing users to rely on it for sensitive situations.</p>
<h3 id="heading-add-a-help-command">Add a Help Command</h3>
<p>A good bot should explain itself.</p>
<pre><code class="language-python">@bot.command()
async def commands_help(ctx):
    await ctx.send(
        "**Available commands:**\n"
        "`!hello` - Say hello\n"
        "`!story` - Start a new story\n"
        "`!choose left` - Choose the left path\n"
        "`!choose right` - Choose the right path\n"
        "`!chat &lt;message&gt;` - Have a casual conversation\n"
        "`!support &lt;message&gt;` - Get general wellness support"
    )
</code></pre>
<p>There's one small issue.</p>
<p>Discord's default help command is already called <code>help</code>.</p>
<p>So instead of:</p>
<pre><code class="language-python">async def help(ctx):
</code></pre>
<p>we've named ours:</p>
<pre><code class="language-python">commands_help
</code></pre>
<p>If you want the command itself to be called <code>!help</code>, you can write:</p>
<pre><code class="language-python">@bot.command(name="help")
async def commands_help(ctx):
    ...
</code></pre>
<p>That tells Discord:</p>
<blockquote>
<p>Use <code>!help</code> for this function even though the Python function has another name.</p>
</blockquote>
<h3 id="heading-improve-error-handling">Improve Error Handling</h3>
<p>Bots shouldn't crash just because someone enters an invalid command.</p>
<p>Add:</p>
<pre><code class="language-python">@bot.event
async def on_command_error(ctx, error):
    if isinstance(error, commands.MissingRequiredArgument):
        await ctx.send(
            "You're missing something. Try `!help` to see how the command works."
        )

    elif isinstance(error, commands.CommandNotFound):
        return

    else:
        print(f"Error: {error}")
</code></pre>
<p>Now if someone types:</p>
<pre><code class="language-text">!chat
</code></pre>
<p>without giving the bot a message, it can respond with a useful explanation instead of dumping a confusing error into the conversation.</p>
<h2 id="heading-put-everything-together">Put Everything Together</h2>
<p>At this point, your <code>bot.py</code> can look like this:</p>
<pre><code class="language-python">import os
import random

import discord
from discord.ext import commands
from dotenv import load_dotenv


load_dotenv()

TOKEN = os.getenv("DISCORD_TOKEN")

if not TOKEN:
    raise RuntimeError("DISCORD_TOKEN is not set.")


intents = discord.Intents.default()
intents.message_content = True

bot = commands.Bot(
    command_prefix="!",
    intents=intents
)


story_locations = [
    "an abandoned library",
    "a mysterious island",
    "a futuristic city",
    "a hidden underground laboratory",
    "a forest that never appears on maps"
]

story_items = [
    "a glowing key",
    "an ancient notebook",
    "a strange compass",
    "a locked metal box",
    "a mysterious photograph"
]

story_events = [
    "You hear footsteps behind you.",
    "The lights suddenly turn off.",
    "A hidden door opens nearby.",
    "Your phone starts displaying a message from an unknown sender.",
    "You notice that the room has changed."
]


user_stories = {}


chat_responses = {
    "hello": [
        "Hey! What's up?",
        "Hello! How's your day going?",
        "Hi! What are you working on?"
    ],

    "python": [
        "Python is a great language for beginners because its syntax is pretty readable.",
        "If you're learning Python, try building something instead of only watching tutorials."
    ],

    "discord": [
        "Discord bots are a fun way to practice Python because you get instant feedback.",
        "Once you understand commands and events, you can build some surprisingly complex bots."
    ]
}


support_responses = {
    "stress": [
        "That sounds like a lot to handle. Try breaking the situation into one small task at a time.",
        "When everything feels overwhelming, it can help to pause and focus on what needs attention right now."
    ],

    "school": [
        "School can pile up quickly. Consider choosing one assignment to work on first instead of trying to solve everything at once.",
        "If school stress is getting difficult to manage, talking with a trusted person can make things feel less like something you have to handle alone."
    ],

    "sad": [
        "I'm sorry you're having a difficult moment. Taking a short break, doing something calming, or talking with someone you trust may help.",
        "You don't have to solve everything immediately. Give yourself some time and consider reaching out to someone you trust."
    ]
}


@bot.event
async def on_ready():
    print(f"Logged in as {bot.user}")


@bot.command()
async def hello(ctx):
    await ctx.send("Hello! I'm online.")


@bot.command()
async def story(ctx):
    user_id = ctx.author.id

    location = random.choice(story_locations)
    item = random.choice(story_items)
    event = random.choice(story_events)

    user_stories[user_id] = {
        "location": location,
        "item": item,
        "event": event
    }

    await ctx.send(
        f"You wake up in {location}.\n\n"
        f"Next to you is {item}.\n\n"
        f"{event}\n\n"
        "What do you do?"
    )


@bot.command()
async def choose(ctx, choice: str):
    user_id = ctx.author.id

    if user_id not in user_stories:
        await ctx.send(
            "You don't have an active story. Try `!story` first."
        )
        return

    choice = choice.lower()

    if choice == "left":
        response = (
            "You head left and discover a room filled with old maps. "
            "One of them has your name written on it."
        )

    elif choice == "right":
        response = (
            "You head right and find a staircase leading toward "
            "a strange blue light."
        )

    else:
        response = "Try choosing `left` or `right`."

    await ctx.send(response)


@bot.command()
async def chat(ctx, *, message: str):
    text = message.lower()

    for keyword, responses in chat_responses.items():
        if keyword in text:
            await ctx.send(random.choice(responses))
            return

    await ctx.send(
        "I'm still learning how to respond to that. "
        "Try talking to me about Python or Discord!"
    )


@bot.command()
async def support(ctx, *, message: str):
    text = message.lower()

    for keyword, responses in support_responses.items():
        if keyword in text:
            response = random.choice(responses)

            await ctx.send(
                f"{response}\n\n"
                "I'm a bot, not a therapist or medical professional. "
                "If you need personal support, consider talking with "
                "someone you trust."
            )
            return

    await ctx.send(
        "It sounds like something is bothering you. "
        "I can offer general wellness suggestions, but I'm not a therapist. "
        "If you need personal support, consider reaching out to someone you trust."
    )


@bot.command(name="help")
async def commands_help(ctx):
    await ctx.send(
        "**Available commands:**\n"
        "`!hello` - Say hello\n"
        "`!story` - Start a new story\n"
        "`!choose left` - Choose the left path\n"
        "`!choose right` - Choose the right path\n"
        "`!chat &lt;message&gt;` - Have a casual conversation\n"
        "`!support &lt;message&gt;` - Get general wellness support"
    )


@bot.event
async def on_command_error(ctx, error):
    if isinstance(error, commands.MissingRequiredArgument):
        await ctx.send(
            "You're missing something. Try `!help` to see how the command works."
        )

    elif isinstance(error, commands.CommandNotFound):
        return

    else:
        print(f"Error: {error}")


bot.run(TOKEN)
</code></pre>
<p>This is enough to create a surprisingly capable beginner Discord project.</p>
<p>But there's an important limitation.</p>
<h2 id="heading-our-bot-doesnt-actually-remember-anything">Our Bot Doesn't Actually Remember Anything</h2>
<p>There's one small problem with our bot so far: it doesn't actually remember anything after it shuts down.</p>
<p>Right now, we're storing our story information in a Python dictionary:</p>
<pre><code class="language-python">user_stories = {}
</code></pre>
<p>This works while the bot is running. But if you stop the program and start it again, the dictionary starts empty.</p>
<p>To fix this, we need somewhere to permanently store our data. That's where a <strong>database</strong> comes in.</p>
<p>For this project, we'll use <strong>SQLite</strong>. SQLite is a lightweight database that stores information in a file on your computer. Python already includes SQLite through the built-in <code>sqlite3</code> module, so we don't need to install anything extra.</p>
<h3 id="heading-create-the-database">Create the Database</h3>
<p>First, add this import near the top of <code>bot.py</code>:</p>
<pre><code class="language-python">import sqlite3
</code></pre>
<p>Then create a connection to a database file:</p>
<pre><code class="language-python">db = sqlite3.connect("bot.db")
cursor = db.cursor()
</code></pre>
<p>The first line creates a database file called <code>bot.db</code> if one doesn't already exist. If the file already exists, SQLite simply opens it.</p>
<p>The second line creates a <strong>cursor</strong>. You can think of the cursor as the part of our Python program that lets us send instructions to the database.</p>
<p>Now we need to create a table where we can store our users' story information:</p>
<pre><code class="language-python">cursor.execute("""
    CREATE TABLE IF NOT EXISTS user_stories (
        user_id INTEGER PRIMARY KEY,
        location TEXT,
        item TEXT,
        event TEXT
    )
""")

db.commit()
</code></pre>
<p>Let's break this down.</p>
<p><code>cursor.execute()</code> tells SQLite to run the SQL command inside the parentheses.</p>
<p>The SQL command starts with:</p>
<pre><code class="language-sql">CREATE TABLE IF NOT EXISTS user_stories
</code></pre>
<p>This tells SQLite to create a table called <code>user_stories</code>, but only if that table doesn't already exist.</p>
<p>Inside the parentheses, we define the information that each row can contain:</p>
<pre><code class="language-sql">user_id INTEGER PRIMARY KEY,
location TEXT,
item TEXT,
event TEXT
</code></pre>
<p><code>user_id</code> stores the Discord user's ID. We use it as the <code>PRIMARY KEY</code>, which means each user gets their own unique row.</p>
<p><code>location</code>, <code>item</code>, and <code>event</code> are all pieces of information about the user's current story.</p>
<p>Finally:</p>
<pre><code class="language-python">db.commit()
</code></pre>
<p>saves the changes to the database.</p>
<p>At this point, your project folder should contain a new file called:</p>
<pre><code class="language-text">bot.db
</code></pre>
<p>You don't need to open or edit this file manually. SQLite will manage it for us.</p>
<h3 id="heading-save-a-users-story">Save a User's Story</h3>
<p>Now let's actually put information into our database.</p>
<p>Suppose we have these variables:</p>
<pre><code class="language-python">user_id = ctx.author.id
location = "an abandoned castle"
item = "a mysterious key"
event = "a locked door"
</code></pre>
<p>We can save them using:</p>
<pre><code class="language-python">cursor.execute(
    """
    INSERT OR REPLACE INTO user_stories
    (user_id, location, item, event)
    VALUES (?, ?, ?, ?)
    """,
    (user_id, location, item, event)
)

db.commit()
</code></pre>
<p>The SQL statement tells SQLite to insert the information into the <code>user_stories</code> table.</p>
<p>The <code>?</code> symbols are placeholders for the actual values. The values are provided separately here:</p>
<pre><code class="language-python">(user_id, location, item, event)
</code></pre>
<p>This is safer than manually inserting values directly into the SQL string.</p>
<p><code>INSERT OR REPLACE</code> also means that if this user already has a saved story, their old story information can be replaced with the new information.</p>
<h3 id="heading-get-the-story-back">Get the Story Back</h3>
<p>Saving information is only half of the job. We also need to be able to retrieve it.</p>
<p>We can search the database for a user's story like this:</p>
<pre><code class="language-python">cursor.execute(
    """
    SELECT location, item, event
    FROM user_stories
    WHERE user_id = ?
    """,
    (user_id,)
)

story = cursor.fetchone()
</code></pre>
<p>This time, we're using <code>SELECT</code> to ask SQLite for information.</p>
<p>The <code>WHERE</code> part is important:</p>
<pre><code class="language-sql">WHERE user_id = ?
</code></pre>
<p>It tells SQLite to find the row belonging to this specific Discord user.</p>
<p>Then:</p>
<pre><code class="language-python">story = cursor.fetchone()
</code></pre>
<p>gets the first matching result.</p>
<p>If the user has a saved story, <code>story</code> will contain their information. If they don't, <code>story</code> will be <code>None</code>.</p>
<p>We can check for that:</p>
<pre><code class="language-python">if story:
    location, item, event = story

    await ctx.send(
        f"You're currently in {location}. "
        f"You have {item}, and you're facing {event}."
    )
else:
    await ctx.send("I don't have a saved story for you yet!")
</code></pre>
<p>Now the bot can retrieve information that was saved earlier, even after the Python program has been restarted.</p>
<h3 id="heading-put-it-into-a-command">Put It Into a Command</h3>
<p>We can turn this into a simple command that lets users check their saved story:</p>
<pre><code class="language-python">@bot.command()
async def status(ctx):
    user_id = ctx.author.id

    cursor.execute(
        """
        SELECT location, item, event
        FROM user_stories
        WHERE user_id = ?
        """,
        (user_id,)
    )

    story = cursor.fetchone()

    if story:
        location, item, event = story

        await ctx.send(
            f"You're currently in {location}. "
            f"You have {item}, and you're facing {event}."
        )
    else:
        await ctx.send(
            "You don't have a saved story yet. "
            "Start one with `!story`!"
        )
</code></pre>
<p>Now a user can type:</p>
<pre><code class="language-text">!status
</code></pre>
<p>and the bot can look up their story from the database.</p>
<p>This is a big improvement over our original dictionary. A dictionary only remembers information while the Python program is running. SQLite lets us save that information so it can still be there when the bot starts again.</p>
<p>For a larger bot, you could eventually store things like user preferences, story progress, inventory, conversation history, or other data. But for now, this simple database is enough to give our bot some real memory.</p>
<h2 id="heading-adding-real-ai-chat">Adding Real AI Chat</h2>
<p>Before we connect our bot to an AI model, let's quickly talk about <strong>Hugging Face</strong>.</p>
<p>If you've never used it before, Hugging Face is a platform where developers can find, share, and use machine learning models and datasets. Think of it as a huge community and library for AI tools.</p>
<p>Hugging Face also provides tools that let Python programs communicate with these models without having to build and train an AI model from scratch.</p>
<p>For our bot, we'll use Hugging Face's <strong>Inference Providers</strong> to send a user's message to a supported language model and receive its response.</p>
<p>We won't be training an AI model ourselves. Instead, we'll use an existing model and connect it to our Discord bot through Python.</p>
<p>Now that we know what Hugging Face is, let's connect it to our bot.</p>
<h3 id="heading-install-the-hugging-face-library">Install the Hugging Face Library</h3>
<p>First, install <code>huggingface_hub</code>:</p>
<pre><code class="language-bash">pip install -U huggingface_hub
</code></pre>
<p>We already installed <code>python-dotenv</code>, so we can use the same <code>.env</code> file from earlier to keep our Hugging Face token out of the source code.</p>
<p>Add your Hugging Face token to <code>.env</code>:</p>
<pre><code class="language-text">DISCORD_TOKEN=YOUR_BOT_TOKEN_HERE
HF_TOKEN=YOUR_HUGGING_FACE_TOKEN_HERE
</code></pre>
<p>Replace <code>YOUR_HUGGING_FACE_TOKEN_HERE</code> with your actual Hugging Face access token.</p>
<p>Just like your Discord bot token, <strong>don't share this token or upload it to GitHub</strong>.</p>
<h3 id="heading-create-the-hugging-face-client">Create the Hugging Face Client</h3>
<p>Now add this import near the top of <code>bot.py</code>:</p>
<pre><code class="language-python">from huggingface_hub import InferenceClient
</code></pre>
<p>Then load the token:</p>
<pre><code class="language-python">HF_TOKEN = os.getenv("HF_TOKEN")

if not HF_TOKEN:
    raise RuntimeError("HF_TOKEN is not set.")
</code></pre>
<p>The first line gets the token from our environment variables. The <code>if</code> statement checks whether the token actually exists. If it doesn't, Python stops and gives us a useful error instead of letting the program fail later in a confusing way.</p>
<p>Now create the Hugging Face client:</p>
<pre><code class="language-python">client = InferenceClient(
    api_key=HF_TOKEN
)
</code></pre>
<p>The <code>InferenceClient</code> is what our Python program will use to communicate with Hugging Face's inference service.</p>
<h3 id="heading-connect-the-ai-model-to-the-bot">Connect the AI Model to the Bot</h3>
<p>Now we can replace our previous keyword-based <code>!chat</code> command with one that sends the user's message to a language model.</p>
<pre><code class="language-python">@bot.command()
async def chat(ctx, *, message: str):
    try:
        response = client.chat_completion(
            model="YOUR_SUPPORTED_MODEL_ID",
            messages=[
                {
                    "role": "system",
                    "content": (
                        "You are a friendly Discord bot. "
                        "Keep responses helpful, concise, and conversational."
                    )
                },
                {
                    "role": "user",
                    "content": message
                }
            ],
            max_tokens=200
        )

        answer = response.choices[0].message.content

        await ctx.send(answer)

    except Exception as error:
        print(f"AI error: {error}")
        await ctx.send(
            "I couldn't generate a response right now. "
            "Please try again later."
        )
</code></pre>
<p>There's quite a bit happening here, so let's walk through it.</p>
<p>We start with the same command structure we've already used:</p>
<pre><code class="language-python">@bot.command()
async def chat(ctx, *, message: str):
</code></pre>
<p>This creates our <code>!chat</code> command and stores everything the user types after it in <code>message</code>.</p>
<p>For example:</p>
<pre><code class="language-text">!chat What is Python?
</code></pre>
<p>gives us:</p>
<pre><code class="language-python">message = "What is Python?"
</code></pre>
<p>Next, we use:</p>
<pre><code class="language-python">try:
</code></pre>
<p>This tells Python that we're about to run code that could potentially fail. Since we're communicating with an external service, things like an unavailable model, an invalid token, or a temporary connection problem can happen.</p>
<p>Now we call:</p>
<pre><code class="language-python">response = client.chat_completion(
</code></pre>
<p>This sends a chat-completion request to the model through Hugging Face. The <code>messages</code> parameter contains the conversation we want the model to respond to.</p>
<p>The first message has the role <code>"system"</code>:</p>
<pre><code class="language-python">{
    "role": "system",
    "content": (
        "You are a friendly Discord bot. "
        "Keep responses helpful, concise, and conversational."
    )
}
</code></pre>
<p>The system message gives the model instructions about how it should respond.</p>
<p>Then we provide the user's actual message:</p>
<pre><code class="language-python">{
    "role": "user",
    "content": message
}
</code></pre>
<p>If the user typed:</p>
<pre><code class="language-text">!chat What is Python?
</code></pre>
<p>then <code>message</code> contains:</p>
<pre><code class="language-text">What is Python?
</code></pre>
<p>So the model receives that as the user's input.</p>
<p>We also have:</p>
<pre><code class="language-python">max_tokens=200
</code></pre>
<p>This limits how much text the model can generate for one response. Keeping responses relatively short works well for Discord because huge blocks of text aren't always very pleasant to read in a chat channel.</p>
<p>You also need to replace:</p>
<pre><code class="language-python">model="YOUR_SUPPORTED_MODEL_ID"
</code></pre>
<p>with the ID of a model currently available through the Hugging Face Inference Providers you are using. Hugging Face's documentation shows that <code>InferenceClient</code> can use a model ID hosted on the Hugging Face Hub for chat completion.</p>
<p>Once the request is complete, we need to get the actual text from the response:</p>
<pre><code class="language-python">answer = response.choices[0].message.content
</code></pre>
<p>The response contains information about the model's output. <code>choices[0]</code> gets the first generated response, and <code>.message.content</code> gives us the actual text.</p>
<p>Then we send it to Discord:</p>
<pre><code class="language-python">await ctx.send(answer)
</code></pre>
<p>So the whole process looks like this:</p>
<pre><code class="language-text">User types !chat
        ↓
Discord sends the command to our bot
        ↓
Python gets the user's message
        ↓
Hugging Face receives the message
        ↓
The AI model generates a response
        ↓
Python gets the generated text
        ↓
The bot sends it back to Discord
</code></pre>
<h3 id="heading-handle-ai-errors">Handle AI Errors</h3>
<p>The last part of our command is:</p>
<pre><code class="language-python">except Exception as error:
    print(f"AI error: {error}")
    await ctx.send(
        "I couldn't generate a response right now. "
        "Please try again later."
    )
</code></pre>
<p>If something goes wrong inside the <code>try</code> block, Python jumps to the <code>except</code> block instead of crashing the entire bot.</p>
<p>The error is printed in the terminal so you can investigate what happened:</p>
<pre><code class="language-python">print(f"AI error: {error}")
</code></pre>
<p>Meanwhile, the Discord user gets a simple message:</p>
<pre><code class="language-text">I couldn't generate a response right now. Please try again later.
</code></pre>
<p>This is much better than letting an API error take down the whole bot.</p>
<p>At this point, you have a real AI-powered <code>!chat</code> command. You can type something like:</p>
<pre><code class="language-text">!chat Tell me an interesting fact about space.
</code></pre>
<p>and the model can generate a response instead of choosing from a small list of pre-written messages.</p>
<p>One thing to remember is that this bot is sending user messages to an external AI service. Don't automatically send private or sensitive conversations to an AI provider. If you make this bot available to other people, be clear about what information it processes and avoid storing or sending more data than the bot actually needs.</p>
<p>You can also combine this AI system with the SQLite database from earlier. For example, you could save a limited amount of conversation history and send relevant previous messages along with a new message. That would allow the bot to keep some context between messages instead of treating every message as a completely new conversation.</p>
<h2 id="heading-how-do-we-keep-the-bot-online">How Do We Keep the Bot Online?</h2>
<p>Here's where the phrase "online forever" needs a little clarification.</p>
<p>There are two different situations.</p>
<h3 id="heading-option-1-run-it-on-your-computer">Option 1: Run It on Your Computer</h3>
<p>When you run:</p>
<pre><code class="language-bash">python bot.py
</code></pre>
<p>the bot stays online while that program is running.</p>
<p>Close the terminal?</p>
<p>Bot goes offline.</p>
<p>Turn off the computer?</p>
<p>Bot goes offline.</p>
<p>Lose internet?</p>
<p>Bot goes offline.</p>
<p>This is perfect for development but it's not a 24/7 production setup.</p>
<h3 id="heading-option-2-host-it-on-a-server">Option 2: Host It on a Server</h3>
<p>For a bot that should stay online while your computer is off, you need a computer somewhere that stays available.</p>
<p>That computer can be a cloud server.</p>
<p>You upload your project, install the dependencies, add your environment variables, and start:</p>
<pre><code class="language-bash">python bot.py
</code></pre>
<p>Now the cloud machine runs the program instead of your laptop.</p>
<p>Services designed for continuously running workloads can be used for this kind of application. For example, Render currently provides a <strong>Background Worker</strong> service type for continuously running processes that don't need to receive incoming web traffic.</p>
<p>But you should check the provider's current pricing and service limitations before deploying. Free hosting tiers aren't necessarily designed for an always-on Discord bot, and a "free forever" 24/7 setup isn't something you should assume a hosting platform will provide.</p>
<h2 id="heading-what-forever-actually-means">What "Forever" Actually Means</h2>
<p>There isn't really a magical:</p>
<pre><code class="language-text">ONLINE_FOREVER = True
</code></pre>
<p>setting.</p>
<p>A bot can stay online continuously only as long as the computer or server running it continues operating.</p>
<p>Even a professionally hosted bot can go offline because of:</p>
<ul>
<li><p>Server maintenance</p>
</li>
<li><p>Deployments</p>
</li>
<li><p>Bugs</p>
</li>
<li><p>Network problems</p>
</li>
<li><p>Provider outages</p>
</li>
<li><p>Invalid credentials</p>
</li>
<li><p>API changes</p>
</li>
<li><p>Billing or account issues</p>
</li>
</ul>
<p>So the realistic goal is to keep the bot running automatically and restart it when something goes wrong.</p>
<p>That is what production hosting is designed to help with.</p>
<p>If your provider supports automatic restarts, enable them.</p>
<p>You can also make your Python code fail clearly when an important environment variable is missing:</p>
<pre><code class="language-python">if not TOKEN:
    raise RuntimeError("DISCORD_TOKEN is not set.")
</code></pre>
<p>A clear error is much easier to debug than a mysterious bot that simply doesn't appear online.</p>
<h2 id="heading-dont-try-to-keep-it-awake-with-random-tricks">Don't Try to "Keep It Awake" With Random Tricks</h2>
<p>You may find tutorials suggesting that you deploy a web server and repeatedly ping it from another service to prevent a free hosting instance from sleeping.</p>
<p>Be careful with that approach.</p>
<p>Hosting providers change their free-tier rules, and attempting to work around those limits can violate their terms.</p>
<p>If you need an actually persistent bot, use a hosting option that explicitly supports the workload.</p>
<p>For example, a background worker is designed for continuously running processes. That's much cleaner than trying to convince a web service that your Discord bot is secretly a website.</p>
<h2 id="heading-additional-features-and-where-to-go-next"><strong>Additional Featur</strong>es and Where to Go Next</h2>
<p>Now that you have a working Discord bot, there are plenty of directions you can take the project next.</p>
<p>You could turn the storytelling system into a more complete game by adding an inventory, multiple chapters, puzzles, or different endings. You could also replace text-based commands with Discord slash commands and buttons to make the bot easier to interact with.</p>
<p>If you're interested in AI, you could expand the chat system by giving the bot different personalities, adding carefully limited conversation context, or using AI to generate parts of the stories.</p>
<p>You could also add moderation features, daily story prompts, or other commands that fit the kind of Discord community you're building.</p>
<p>These are ideas for extending the project rather than features we'll build step by step in this tutorial. The important thing is that you now have the foundation to experiment with them yourself.</p>
<p>Start with one small feature, figure out how it works, and build from there. You don't need to turn the bot into a massive project all at once.</p>
<p>The more you experiment with the code, the more you'll start seeing how Python, Discord, databases, and AI can work together in a real application.</p>
<h2 id="heading-test-everything-locally-first">Test Everything Locally First</h2>
<p>Before deploying, test:</p>
<pre><code class="language-text">!hello
!story
!choose left
!choose right
!chat hello
!chat I want to learn Python
!support I'm stressed
!help
</code></pre>
<p>Then test weird inputs:</p>
<pre><code class="language-text">!choose banana
!chat
!support
!unknowncommand
</code></pre>
<p>You want to discover bugs while you're sitting in front of your computer, not three days later when someone tells you:</p>
<blockquote>
<p>"Your bot has been broken since Tuesday."</p>
</blockquote>
<h2 id="heading-deploying-the-bot">Deploying the Bot</h2>
<p>First, make sure your project contains:</p>
<pre><code class="language-text">discord-story-bot/
│
├── bot.py
├── requirements.txt
├── .gitignore
└── .python-version
</code></pre>
<p>A <code>.python-version</code> file can contain something like:</p>
<pre><code class="language-text">3.13
</code></pre>
<p>Using a version file makes your deployment environment more predictable. Render currently supports specifying a Python version through <code>.python-version</code> or an environment variable.</p>
<p>Your <code>requirements.txt</code> should contain your dependencies.</p>
<p>For example:</p>
<pre><code class="language-text">discord.py
python-dotenv
</code></pre>
<p>For deployment, you generally don't need the local <code>.env</code> file.</p>
<p>Instead, add:</p>
<pre><code class="language-text">DISCORD_TOKEN
</code></pre>
<p>as an environment variable in your hosting provider's dashboard.</p>
<p>That way the secret isn't stored inside your repository.</p>
<h3 id="heading-the-start-command">The Start Command</h3>
<p>Your deployment service needs to know what to run.</p>
<p>For this project, the start command is:</p>
<pre><code class="language-bash">python bot.py
</code></pre>
<p>The important thing is that the process doesn't immediately exit.</p>
<p>A Discord bot stays alive because <code>bot.run(TOKEN)</code> starts the Discord connection and keeps the program running.</p>
<p>If your hosting service supports background workers, that's a natural fit for a bot like this because the bot doesn't need to serve normal HTTP requests. Render specifically describes background workers as continuously running services that don't receive incoming network traffic.</p>
<h2 id="heading-remember-keep-your-secrets-secret">Remember: Keep Your Secrets Secret</h2>
<p>This is worth repeating because it causes a lot of beginner projects to get compromised.</p>
<p>Never commit this:</p>
<pre><code class="language-python">bot.run("YOUR_REAL_TOKEN")
</code></pre>
<p>Never upload:</p>
<pre><code class="language-text">.env
</code></pre>
<p>Never paste your actual token into a public GitHub issue.</p>
<p>If a token accidentally becomes public, treat it as compromised and regenerate it.</p>
<p>Environment variables are your friend.</p>
<h2 id="heading-what-you-learned">What You Learned</h2>
<p>You've now built a Discord bot that demonstrates several real programming concepts.</p>
<p>You learned how to:</p>
<ul>
<li><p>Create a Discord application</p>
</li>
<li><p>Connect Python to Discord</p>
</li>
<li><p>Use <code>discord.py</code></p>
</li>
<li><p>Configure Gateway Intents</p>
</li>
<li><p>Create commands</p>
</li>
<li><p>Use asynchronous functions</p>
</li>
<li><p>Read command arguments</p>
</li>
<li><p>Generate random stories</p>
</li>
<li><p>Store temporary user state</p>
</li>
<li><p>Create a basic chat system</p>
</li>
<li><p>Create a mental-wellness support feature</p>
</li>
<li><p>Handle command errors</p>
</li>
<li><p>Keep secrets out of source code</p>
</li>
<li><p>Prepare a project for deployment</p>
</li>
<li><p>Think about persistent hosting</p>
</li>
</ul>
<p>And underneath all those features, the architecture is still surprisingly simple:</p>
<pre><code class="language-text">User sends command
        ↓
Discord receives message
        ↓
discord.py receives event
        ↓
Python function runs
        ↓
Bot generates response
        ↓
Discord displays response
</code></pre>
<p>You don't need thousands of lines of code to get started.</p>
<p>You need a clear idea, a few Python concepts, and the willingness to keep debugging when something inevitably breaks.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>The coolest part of this project isn't really the Discord bot. It's what the project teaches you.</p>
<p>And once you understand the pieces, you can reuse the same ideas in countless projects.</p>
<p>A Discord bot can become a game, which could become a web application, which could also become a larger software project.</p>
<p>And suddenly you're not just learning Python syntax anymore. You're learning how software actually gets built, one command at a time.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Why Many Beginner Self-Taught Developers Struggle (And What to Do About It) ]]>
                </title>
                <description>
                    <![CDATA[ Self‑taught developers often begin with the same “starter pack”: a laptop, internet access, and sheer determination. What they lack, however, is structured guidance, a defined curriculum, or any form  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/why-many-beginner-self-taught-developers-struggle-and-what-to-do-about-it/</link>
                <guid isPermaLink="false">69e66a12c9501dd0101793f9</guid>
                
                    <category>
                        <![CDATA[ Learning Journey ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Self-taught  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ learning ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Great John ]]>
                </dc:creator>
                <pubDate>Mon, 20 Apr 2026 18:01:54 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/76b41ea4-5375-4393-ad06-a0e53b53d23a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Self‑taught developers often begin with the same “starter pack”: a laptop, internet access, and sheer determination. What they lack, however, is structured guidance, a defined curriculum, or any form of pedagogical support.</p>
<p>This absence of direction makes the journey significantly harder. Faced with an overwhelming abundance of online resources, many beginners become confused about where to start and often attempt to learn everything at once.</p>
<p>This is where the struggle with knowledge retention begins.</p>
<p>Not because they lack intelligence or effort, but because they're learning in a way that contradicts how the human brain actually works.</p>
<p>They dive into tutorials and courses without understanding the mechanism of the brain – that is, how the brain processes, stores, and retrieves information. As a result, much of what they learn simply doesn’t stick.</p>
<h3 id="heading-how-the-brain-processes-information">How the Brain Processes Information</h3>
<p>So what's the connection between the brain and learning how to code, you might ponder?</p>
<p>The connection is direct and unavoidable.</p>
<p>Coding isn't learned through willpower or motivation — though both matter — or by spending countless hours watching tutorials.</p>
<p>It's learned through the brain’s ability to process, store, and retrieve information.</p>
<p>Every variable, function, data structure, or debugging pattern must pass through the brain’s cognitive systems before it becomes usable knowledge.</p>
<p>If your learning process doesn't align with how the brain naturally acquires and organises information, your retention will collapse, no matter how determined you are.</p>
<p>Now imagine you’re trying to fill a bucket with water. You keep pouring and pouring, but the bucket has tiny holes at the bottom. No matter how much effort you put in, the water keeps leaking out. You might blame yourself for not pouring fast enough, or you might try switching to a bigger jug, but the real problem isn’t your effort — it’s the bucket.</p>
<p>The water is the information you’re trying to learn.</p>
<p>The bucket is your brain’s memory system.</p>
<p>The holes in the bucket are the natural forgetting mechanisms of the brain: cognitive overload, limited working memory, and other constraints that make retention difficult.</p>
<p>If you don’t understand these mechanisms, you can pour in as much information as you want, but most of it will leak out.</p>
<p>Not because you’re incapable, but because you’re learning in a way that contradicts how the brain actually retains knowledge.</p>
<h3 id="heading-the-role-of-academic-learning-theories">The Role of Academic Learning Theories</h3>
<p>Since learning ultimately takes place in the brain, an important question is: How does the human brain acquire, organize, and apply knowledge and why does the typical self‑taught learning process clash with these principles?</p>
<p>This is where academic learning theories become indispensable. These frameworks explain how the brain actually acquires, retains, and applies complex information and they offer a scientific roadmap for learning more effectively. Without understanding these principles, self‑taught developers unintentionally work against the brain’s natural architecture.</p>
<p>The purpose of this article is to unpack these essential learning theories and apply them directly to the beginner self‑taught developer’s journey.</p>
<p>By understanding how the brain processes information, beginners can structure their learning more intentionally, retain knowledge more reliably, and move toward becoming competent developers with far greater confidence and clarity.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-cognitive-load-theory-clt">Cognitive Load Theory (CLT)</a></p>
<ul>
<li><p><a href="#heading-working-memory">Working Memory</a></p>
</li>
<li><p><a href="#heading-millers-law">Miller's Law</a></p>
</li>
<li><p><a href="#heading-chunking">Chunking</a></p>
</li>
<li><p><a href="#heading-long-term-memory">Long-term memory</a></p>
</li>
<li><p><a href="#heading-schema">Schema</a></p>
</li>
<li><p><a href="#heading-intrinsic-load-the-natural-difficulty-of-the-task">Intrinsic Load: The Natural Difficulty of the Task</a></p>
</li>
<li><p><a href="#heading-extraneous-load-the-mental-noise">Extraneous Load: The Mental Noise</a></p>
</li>
<li><p><a href="#heading-germane-load-the-construction-work">Germane Load: The Construction Work</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-ebbinghaus-forgetting-curve">Ebbinghaus Forgetting Curve.</a></p>
</li>
<li><p><a href="#heading-how-the-theory-of-spaced-repetition-works">How the Theory of Spaced Repetition Works</a></p>
</li>
<li><p><a href="#heading-theory-of-deliberate-practice">Theory of Deliberate Practice</a></p>
</li>
<li><p><a href="#heading-what-is-blooms-taxonomy">What is Bloom's Taxonomy?</a></p>
<ul>
<li><a href="#heading-focused-mode-vs-diffuse-mode">Focused Mode vs Diffuse Mode</a></li>
</ul>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a href="#heading-references">References</a></p>
</li>
</ul>
<h2 id="heading-cognitive-load-theory-clt">Cognitive Load Theory (CLT)</h2>
<p>Learning a new concept often requires mental effort by the brain to process newly acquired information. This effort exerted by the brain is known as cognitive load, a term coined by Australian educational psychologist John Sweller in 1988 during his study on how the brain acquires and retains information (Sweller, 1988).</p>
<p>Since then, his work has been expanded upon by other researchers. Notably, Dylan Wiliam famously tweeted in 2017 that Cognitive Load Theory (CLT) is "the single most important thing for teachers to know" (Dylan William, 2017).</p>
<p>You might wonder again: What does this have to do with me? As a beginner self‑taught developer, the answer is simple: you're both the teacher and the student.</p>
<p>So this is the most important theory you should know. In this self-tutoring journey, you're tasked with designing your own curriculum, choosing your own resources, pace your own learning, and evaluating your own progress.</p>
<p>Without understanding how cognitive load affects your ability to absorb and retain information, you may unintentionally overload your brain and sabotage your own learning.</p>
<p>Before we get into the nitty-gritty of CLT, there are important concepts masterminded by David Geary that you'll need to grasp to sufficiently understand this concept : “that which can be learnt” (biologically primary knowledge), “that which can be taught”(biologically secondary knowledge) (Geary, 2007, 2008).</p>
<p>According Geary (2007, 2008), "biologically primary knowledge" consists of "instinctual" skills that the brain is evolved to pick up naturally without formal schooling.</p>
<p>Examples include learning a first language, recognizing faces, or basic social navigation.</p>
<p>"Biologically secondary knowledge", on the other hand, consists of cultural and technical skills, like reading and writing, that are necessary for society but don't come naturally to the brain.</p>
<p>This is because we aren't "wired" to pick these up automatically. Instead, they require formal instruction and schools to pass them down.</p>
<p>Therefore, coding is a prime example of biologically secondary knowledge. The human brain is remarkably plastic, but it didn't evolve to interpret syntax, manage memory allocation, or debug logical loops.</p>
<p>These are cultural inventions, not natural instincts. Unlike learning to walk or speak your native language (which are biologically primary skills) you can't learn to code simply by “being around” computers.</p>
<p>Recognising that the human brain is not instinctively prepared for coding allows you to change your strategy. Once you accept that coding concepts are not “natural,” you can finally approach them with the structured, deliberate effort they require.</p>
<p>The second set of concepts beginner self-taught developers should know and understand are working memory, Miller's Laws, chunking, long-term memory, and schemas.</p>
<h3 id="heading-working-memory">Working Memory</h3>
<p>Working memory is where thinking happens. It's the active mental workspace where you hold information while you process it. When you encounter concepts like syntax, loops, functions, or an if/elseif statement for the first time, all of that information sits inside your working memory. The problem is that working memory is extremely limited and fragile.</p>
<p>When you first learn to code, your working memory functions like a small mental desk where only a few items can be placed at once.</p>
<p>Imagine trying to assemble a piece of IKEA furniture on a tiny coffee table. If you spread out the instruction manual, the screws, the wooden panels, and the tools all at the same time, the table becomes cluttered instantly. You start losing track of which part goes where, not because you’re incapable, but because the surface you’re working on is too small to hold everything at once.</p>
<p>Working memory behaves the same way. When you’re learning new concepts – like arrays, loops, functions, or error handling – each idea takes up space on that mental desk. If you then overload it, the desk becomes overcrowded.</p>
<p>Once it exceeds its capacity, things begin to fall off, and your ability to retain collapses.</p>
<p>It’s not a lack of intelligence. It’s simply the natural limit of working memory.</p>
<p>Now this collapse happens because you went against the threshold your working memory can hold. This is backed up by research that shows that working memory can typically process only 5–9 pieces of information at any given time (Miller, 1956). This is known as Miller’s Law.</p>
<h3 id="heading-millers-law">Miller's Law</h3>
<p>In 1956, George Miller found that the average human can hold about seven items (plus or minus two) in working memory at once, even some recent research has stated the number is even lower about four item (Nelson Cowan, 2001).</p>
<p>So imagine you encounter a tutorial that introduces the following concepts all at the same time: a Route, a Controller, a Model, a Migration, a View, a Request, Helper files, Jobs and Queues, Middleware, Roles and Permissions, and a Service Provider. If you attempt to hold all of these in your mind simultaneously, you'll inevitably hit Miller’s Wall, as your working memory becomes overloaded, and you'll likely forget the first concept long before you reach the last.</p>
<p>So how do you handle complex tasks if the brain can only juggle 4–9 items at once?</p>
<p>You use chunking — the process of grouping small pieces of information into a single, meaningful unit.</p>
<h3 id="heading-chunking">Chunking</h3>
<p>Chunking is the brain’s strategy for compressing complexity. Instead of forcing working memory to hold a dozen unrelated items, you reorganise them into a few coherent structures. This reduces cognitive load, prevents overload, and allows you to work with far more information than your raw working‑memory limits would normally allow.</p>
<p>Let's consider an example:</p>
<p>A beginner learning Laravel might see Route, Controller, Model, Migration, and View as five separate, overwhelming items. To a beginner, each one feels like a distinct cognitive burden. But an experienced developer doesn't treat them as isolated concepts. Instead, they're understood as a single meaningful unit: the MVC pattern. Instead of holding five items in working memory, the expert holds one.</p>
<p>This raises an important question: how does a beginner know that these five elements belong together when they have only just encountered them?</p>
<p>It's crucial to emphasise that chunking isn't automatic. It depends on recognising meaningful relationships between concepts, and beginners typically lack the prior knowledge needed to perceive those relationships early on.</p>
<p>But as learners repeatedly encounter the same sequence during the learning process, they begin to notice consistent patterns. Over time, the brain’s natural tendency to seek structure enables them to identify which components reliably operate together, allowing these elements to gradually fuse into a single, meaningful chunk.</p>
<p>For example, when I first followed a Laravel e-commerce tutorial, I noticed that for every new resource the tutor created –&nbsp;Payment, Cart, KYC, and Contact – the same pattern was repeated: a Controller, a Model, and a View were always created together.</p>
<p>After encountering this sequence several times, it became clear that these components consistently belonged together as a set. Over time, I began to perceive the Controller, Model, and View not as separate elements, but as a single, integrated unit.</p>
<p>So beginners may not be able to chunk effectively on day one because they lack the prior knowledge needed to recognise what belongs together. But with time, and repeated encounters across different contexts, these individual pieces fuse into stable mental units stored in long‑term memory.</p>
<p>What feels overwhelming at first eventually becomes effortless, not because the task became simpler, but because your internal representation became more organised.</p>
<p>This is the power of chunking: it transforms scattered pieces of information into organised units that fit comfortably within the limits of working memory.</p>
<p>Without chunking, beginners drown in details. With chunking, they gain the cognitive space needed to understand, retain, and apply what they learn.</p>
<h3 id="heading-long-term-memory">Long-term memory</h3>
<p>Unlike working memory, long‑term memory has virtually infinite capacity. The goal of all study is to move information from the cramped working memory into the vast long‑term memory.</p>
<p>Here is the real secret: you don’t learn in working memory – you only process there.</p>
<p>True learning is the permanent change that happens in long‑term memory.</p>
<h3 id="heading-schema">Schema</h3>
<p>Once stored in long-term memory, information becomes part of a schema — a mental map or filing system that organizes related ideas.</p>
<p>For example, when you finally learn that Laravel is an MVC framework, you aren’t just memorizing three letters. You're building a schema that tells your brain: Models handle data, Views handle presentation, and Controllers handle logic.</p>
<p>Once a schema is built, it can be pulled into working memory as a single chunk, effectively bypassing Miller’s Law.</p>
<p>This is how experts think effortlessly while beginners feel overwhelmed.</p>
<p>And this is why Garnett (2020) argues that "being competent or lacking competence in something depends entirely on how secure the retrieval of knowledge held in the schema is".</p>
<p>Now that the foundations of working memory, long‑term memory, schemas, and chunking are clear, we can turn to another set of concepts every self‑taught developer must understand: intrinsic load, extraneous load, and germane load. These three components make up the full structure of Cognitive Load Theory, and they determine whether learning feels manageable or overwhelming.</p>
<h3 id="heading-intrinsic-load-the-natural-difficulty-of-the-task">Intrinsic Load: The Natural Difficulty of the Task</h3>
<p>Intrinsic load refers to the inherent complexity of the material itself. Some concepts are simply harder than others because they contain more interacting elements that must be processed at the same time.</p>
<p>In Laravel, understanding a simple Route has low intrinsic load.</p>
<p>But concepts like Dependency Injection or Polymorphic Relationships have high intrinsic load because they involve multiple layers of abstraction and interdependent ideas.</p>
<p>You can't change the intrinsic load of a concept, but you can manage it by breaking the idea into smaller, more digestible sub‑tasks. This is why good teaching — and good self‑teaching — always begins with simplification and sequencing.</p>
<p>Simplification means stripping a concept down to its essential parts so the learner isn't overwhelmed by unnecessary detail.</p>
<p>Sequencing means introducing parts in a logical order, where each step builds on the previous one. This helps reduce unnecessary cognitive load and allows learners to devote more mental effort (germane load) to building schemas.</p>
<p>It’s like meeting someone new, and they tell you their name and it happens to be your mother’s name. Instantly, your brain forms a connection. You associate this new person’s name with the strong, deeply stored memory of your mother.</p>
<p>Because that schema already exists in your long-term memory, the new information “attaches” to it. Later, when you try to recall the name, you don’t struggle, you simply think of your mother, and the name comes back easily.</p>
<p>While many believe self‑taught developers struggle because they lack immediate, reliable, personal guidance, there's actually a hidden advantage in this predicament. When a teacher explains a concept, even if they try their best to “chunk” the information, they can't truly know the student’s internal limits — how much intrinsic load the learner can handle, how quickly they can process new ideas, or how much prior knowledge they can activate.</p>
<p>This is where self‑taught developer quietly shines. Because you are both the teacher and the student, you know your own cognitive limits better than anyone else. You can slow down when something feels heavy, pause when working memory is overloaded, and chunk information in a way that perfectly matches your personal capacity.</p>
<p>You can simplify a concept to its bare essentials and sequence it at a pace that aligns with your own understanding.</p>
<h3 id="heading-extraneous-load-the-mental-noise">Extraneous Load: The Mental Noise</h3>
<p>Extraneous load is the enemy of the self‑taught developer. It's the mental effort wasted on tasks that don't contribute to actual learning. This is where a self-taught developer's strength must truly shine.</p>
<p>A teacher in a classroom is responsible for removing any distractions that might derail a child or slow down their assimilation of knowledge. As a self-taught developer, that responsibility falls entirely on you. You must identify these distractions and eliminate them.</p>
<p>As a self-taught developer myself, I use specific strategies to ensure I stay focused. Before starting any course, I spend time reading the comment section to see what others have experienced. If I see complaints about low audio quality, unclear explanations, or tutorials that move too fast, I immediately abandon that course and look for one with better reviews.</p>
<p>Anything that might derail my progress must be removed. If you spend half of your mental energy trying to figure out all of these, you only have the remaining half available for understanding the logic of the code. And remember: when learning new concepts, we use working memory, which is fragile.</p>
<p>As your own “inner teacher,” you must eliminate this noise so your limited working memory can focus entirely on the material that matters.</p>
<h3 id="heading-germane-load-the-construction-work">Germane Load: The Construction Work</h3>
<p>Germane load is the productive mental effort used to build and refine schemas — the mental structures that make future learning easier.</p>
<p>This is the “Aha!” moment when new information connects meaningfully to what you already know.</p>
<p>For example, germane load appears when you realise that a Database Migration is essentially a version‑control system for your table structure.</p>
<p>That insight is schema construction in action.</p>
<p>Teachers are often advised to help manage a child's germane load. One way they do this is by connecting the new idea being taught to an existing concept.</p>
<p>By doing this, they help the student build schemas: mental frameworks that organise and interpret information.</p>
<p>For a self-taught developer, this means instead of memorizing a new syntax in isolation, you look for a 'hook' in something you already understand.</p>
<p>For example, if you already know how a physical filing cabinet works, understanding Arrays or Objects in code becomes much easier.</p>
<p>You aren't learning from scratch – you're simply "plugging" new data into an old socket. This reduces the mental strain and makes the new knowledge stick permanently.</p>
<p>But this can only happen when intrinsic load is properly managed and extraneous load is removed.</p>
<p>It's important to note that, unlike intrinsic and extraneous load, germane load isn't an independent type of cognitive load.</p>
<p>Instead, it represents the portion of your working memory that remains available to handle the element interactivity associated with intrinsic load.</p>
<p>In other words, germane load is the mental energy you have left for learning once the unnecessary noise is stripped away.</p>
<p>Understanding cognitive load explains why learning can feel overwhelming in the moment, but it doesn't explain why knowledge fades after the moment has passed. For that, we turn to another foundational principle in learning science: the Ebbinghaus Forgetting Curve.</p>
<h2 id="heading-ebbinghaus-forgetting-curve">Ebbinghaus Forgetting Curve</h2>
<p>If you remember the bucket analogy, this curve represents one of the holes at the bottom — the brain’s natural tendency to let information leak away unless it's reinforced.</p>
<p>In the late 19th century, Hermann Ebbinghaus discovered that human memory follows a predictable pattern of decline. After learning something new, we forget most of it astonishingly quickly — often within hours — unless the information is revisited. The forgetting curve shows that memory retention drops sharply at first and then continues to decline more slowly over time.</p>
<p>Studies based on Ebbinghaus’ forgetting curve found that without a conscious effort to retain newly acquired information, we lose approximately 50% of new information within 24 hours, and up to 90% within a week (Clearwater, 2024).</p>
<p>In other words, the brain is designed to discard information that isn't reinforced.</p>
<p>For self‑taught developers, this has profound implications.</p>
<p>You may understand a Laravel controller today, spatial roles and permission concepts, and so on – but if you don't revisit it, practice it, or apply it within the next few hours, your brain will naturally let it fade.</p>
<p>This is not a sign of weakness or lack of talent. It's simply how human brain works.</p>
<p>The forgetting curve also explains why tutorials feel deceptively easy the moment you're going through them.</p>
<p>While watching, everything seems clear — but a week later, the same concepts feel unfamiliar.</p>
<p>The knowledge never made it into long‑term memory because you didn't revisit, practice, or connect it to existing schemas.</p>
<p>Since the human brain is designed to forget anything that isn't repeated, repetition becomes the signal that tells the brain, “This matters — keep it.”</p>
<p>This is why, when you meet someone for the first time and they tell you their name, you'll almost certainly forget it unless you consciously repeat it to yourself several times. If you don’t reinforce it, you end up asking — often with embarrassment — “Sorry, what was your name again?”</p>
<p>The same principle applies to learning code: without deliberate repetition, the brain simply lets the information fade. However, with a technique called spaced repetition, retention is significantly improved .</p>
<h2 id="heading-how-the-theory-of-spaced-repetition-works">How the Theory of Spaced Repetition Works</h2>
<p>Spaced repetition is a learning technique grounded in cognitive psychology that involves reviewing information at increasingly spaced intervals to strengthen long‑term memory retention.</p>
<p>It's based on the principle that memory decays predictably over time — as demonstrated by the Ebbinghaus Forgetting Curve — and that strategically timed reviews or repetition interrupt this decay, making the memory more durable with each repetition.</p>
<p>This idea is what gave birth to Anki-Flash cards.</p>
<p>Imagine you're trying to memorise the time complexity of different algorithms.</p>
<p>This is a classic "dry" academic topic that's easy to forget.</p>
<p>To understand why spaced repetition is so powerful, consider a familiar scenario. You spend Sunday night staring at a chart of Big‑O complexities for four hours. By Monday’s review, you can recall most of them. By Friday, only a few remain. Two weeks later, the entire chart has vanished from memory.</p>
<p>Spaced repetition reverses this process by reviewing information at the precise moment it's about to be forgotten. Instead of cramming Big‑O notation in a single session, you revisit it across expanding intervals:</p>
<ol>
<li><p>Day 1 (Initial Learning): You study the Big‑O chart and understand each complexity class.</p>
</li>
<li><p>Day 2 (First Review): You test yourself. If you recall an item correctly, you schedule the next review three days later. If you miss it, you review it again the following day.</p>
</li>
<li><p>Day 5 (Second Review): You encounter the material again. Because you still remember it, the interval expands to ten days.</p>
</li>
<li><p>Day 15 (Third Review): Your memory has begun to fade, but the moment you see the prompt, the concept resurfaces. This slight struggle to retrieve the information is precisely what strengthens long‑term retention.</p>
</li>
<li><p>Day 45 (Fourth Review): By now, the memory is deeply consolidated. Concepts like O(log⁡n) feel as natural and accessible as your own phone number.</p>
</li>
</ol>
<p>Through this process, spaced repetition transforms fragile, short‑term awareness into durable, long‑term knowledge. Each review interrupts the forgetting curve, reinforces the schema, and reduces the cognitive load required to recall the concept in the future.</p>
<p>For self-taught developers, spaced repetition can take many forms. You might rewrite code from memory, re‑implement a feature days later, build small variations of the same concept, or return to the concept after working on different tasks.</p>
<p>Every review strengthens the schema and reduces the cognitive load required to recall it. Over time, what once felt complex becomes automatic — not because the concept changed, but because your brain reorganised it into a stable, efficient structure.</p>
<p>As you can see, learning isn't a single event but a cycle of exposure, forgetting, and reinforcement.</p>
<p>Mastery comes not from seeing something once, but from returning to it until it becomes part of your cognitive architecture.</p>
<p>But we must be careful with repetition. Doing the same thing over and over again doesn't guarantee improvement. In fact, mindless repetition can trap you at the same level indefinitely.</p>
<p>This is where the theory of deliberate practice becomes essential, as it emphasises increasing the level of challenge, focusing on specific weaknesses, and actively seeking feedback so that each repetition leads to measurable improvement rather than just familiarity.</p>
<h2 id="heading-theory-of-deliberate-practice">Theory of Deliberate Practice</h2>
<p>Developed by psychologist K. Anders Ericsson, it argues that expertise is not the result of talent but of high‑quality, intentional practice (Ericsson, 1993). This type of practice is fundamentally different from simply doing something repeatedly.</p>
<p>He coined the term "deliberate practice" while researching how people become experts. Studying experts from several different fields, he dismantled the myth that expert performers have unusual innate talents.</p>
<p>Instead, he discovered that experts attain their high performance through how they practice: it's a deliberate effort to become an expert. This effort is characterized by breaking down required skills into smaller parts and practicing these parts repeatedly.</p>
<p>According to Anders Ericsson, Deliberate Practice requires:</p>
<ol>
<li><p>Clear goals</p>
</li>
<li><p>Immediate feedback</p>
</li>
<li><p>Tasks that stretch your ability just beyond your comfort zone</p>
</li>
<li><p>Full concentration and effort</p>
</li>
</ol>
<p>The main tenet of Deliberate Practice is that tasks must stretch your ability just beyond your comfort zone. This is paramount to the advancement of learning.</p>
<p>Imagine a child being taught 1+1 every day. That child will never grow beyond basic arithmetic. Anders Ericsson calls this "Arrested Development" (Ericsson, Nandagopal and Roring, 2005). For that child to grow to become a mathematician, their knowledge must be stretched.</p>
<p>The takeaway for developers is a play on the DRY principle (Don’t Repeat Yourself): If you are only repeating what you already know without stretching yourself, you aren't growing. This "stretch" is the extra edge that Deliberate Practice adds to Spaced Repetition.</p>
<p>Building a simple to-do list, a calculator, or a weather app over and over again won't take you anywhere. You already know how to do those.</p>
<p>To truly grow, you must stretch yourself. Instead, try a project that integrates new ideas, like building a mini-app where the weather data affects your to-do list. For example, if the API shows it's raining, the app automatically hides outdoor tasks and calculates the time you'll save or the indoor tasks you should prioritize instead.</p>
<p>This forces you to handle complex logic and state management, moving you beyond simple repetition into true mastery.</p>
<p>This ability to create brings me to the last theory: Bloom's Taxonomy.</p>
<h2 id="heading-what-is-blooms-taxonomy">What is Bloom's Taxonomy?</h2>
<p>Bloom’s Taxonomy provides a hierarchy of cognitive skills that learners move through as they develop mastery. It begins with the simplest tasks and progresses toward the most complex:</p>
<ol>
<li><p>Remember – recalling facts or syntax</p>
</li>
<li><p>Understand – explaining concepts in your own words</p>
</li>
<li><p>Apply – using knowledge in real situations</p>
</li>
<li><p>Analyze – breaking problems into parts</p>
</li>
<li><p>Evaluate – judging solutions or comparing approaches</p>
</li>
<li><p>Create – building original systems or applications</p>
</li>
</ol>
<p>Most self‑taught developers get stuck in the first two levels. They memorize syntax and understand examples, but they struggle to apply, analyze, or create.</p>
<p>This isn't because they lack ability. Rather, it's because they haven't been taught that learning must progress through these stages.</p>
<p>Bloom’s Taxonomy gives structure to the learning journey.</p>
<p>It reminds self-taught developers that mastery isn't achieved by watching tutorials but by climbing the ladder from remembering → understanding → applying → analyzing → evaluating → creating (with an emphasis on Creation).</p>
<p>Creation is one of the most difficult yet most transformative experiences in your journey as a developer. It forces you to think abstractly, confront ambiguity, and notice dimensions of a problem that tutorials rarely reveal.</p>
<p>When you build something real, the neatness of the theory in your head collapses, and you begin to see its true complexity. You must then devise strategies to navigate these challenges, and through this process, you learn.</p>
<p>And as with anything worthwhile, the process isn't smooth. You'll encounter bugs — not just one or two, but hundreds. Yet this is precisely how real knowledge is built. Every bug you solve becomes a permanent entry in your long‑term memory.</p>
<p>The next time you see that error, you won’t panic. Instead, you’ll recognise it instantly and know exactly where it’s coming from and how to fix it.</p>
<p>Some self‑taught developers encounter a few bugs and never return to their projects again, concluding that “coding isn’t for me.”</p>
<p>After trying several fixes and seeing no progress, they abandon the work and look for something else. But this is the wrong conclusion. The problem is rarely a lack of talent — it's a misunderstanding of how the brain behaves under cognitive strain.</p>
<h3 id="heading-focused-mode-vs-diffuse-mode">Focused Mode vs Diffuse Mode</h3>
<p>When you spend a long time wrestling with a bug, you may be experiencing mental fixation or functional fixedness.</p>
<p>This is when your brain becomes locked into a single line of reasoning, repeating the same logic path over and over because it feels like the right direction. The longer you stare at the problem, the deeper the cognitive rut becomes. You develop tunnel vision, making it almost impossible to see alternative solutions.</p>
<p>This is where understanding how the brain operates becomes essential.</p>
<p>According to Oakley (2014), the brain works in two primary modes:</p>
<ol>
<li><p>Focused Mode: Ideal for executing a known formula or following a clear procedure, terrible for discovering a new approach or breaking out of a mental rut.</p>
</li>
<li><p>Diffuse Mode: This is activated when you step away — walking, showering, relaxing, or sleeping.</p>
</li>
</ol>
<p>In this second mode, the brain enters a “big‑picture” state where neural connections stretch across different regions.</p>
<p>The background processes continue working on the problem without the restrictive tunnel vision of conscious focus.</p>
<p>This phenomenon is known as incubation.</p>
<p>This is why solutions often appear when you’re not actively thinking about the problem. You step away, and suddenly the answer emerges, not because you stopped working, but because a different part of your brain started working for you.</p>
<p>The reality is that many developers never allow for incubation. While you step away from the problem, your brain performs subconscious synthesis: it clears out the noise (Extraneous Load) and lets the core logic (Germane Load) settle. When you return, the “wrong” paths you were obsessing over have faded, and the correct path which was there all along often finally becomes visible.</p>
<p>This is why developers must deliberately allow for incubation. We can take some lessons from great minds of the past:</p>
<p>Henri Poincaré famously struggled with Fuchsian functions for weeks. It was only during a geological excursion when he had completely forgotten about the mathematics that the solution appeared with “perfect certainty” the moment he stepped onto an omnibus. His breakthrough did not come from more effort, but from stepping away long enough for diffuse mode to take over.</p>
<p>Friedrich August Kekulé experienced something similar after years of wondering why benzene’s carbon atoms didn't fit a linear structure.</p>
<p>If some of the greatest minds in history stepped away from their problems and found solutions in diffuse mode, why should developers treat themselves any differently?</p>
<p>Now that you're familiar with some key learning strategies –&nbsp;Cognitive Load Theory, Spaced Repetition, and Bloom's Taxonomy –&nbsp;creating or building a project from the ground up should be your next task. It will help you curate, retrieve, organise, and seal in all that diverse knowledge you've gathered as a self-taught developer.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, we explored why the human brain isn't instinctively wired to understand programming. Coding is a biologically secondary skill, which means it doesn't develop naturally through immersion but requires explicit instruction, structure, and patience.</p>
<p>We also talked about the limits of working memory, the importance of chunking, and the need to manage cognitive load so that learning remains possible rather than overwhelming.</p>
<p>We then analyzed the three components of Cognitive Load Theory –&nbsp;intrinsic, extraneous, and germane load –&nbsp;and discussed how each influences the learning process. Reducing extraneous load is especially crucial for self‑taught developers, as it frees up mental resources for meaningful understanding.</p>
<p>From there, we turned to the Ebbinghaus Forgetting Curve, which demonstrates how quickly newly learned information fades without reinforcement.</p>
<p>To counter this natural forgetting, we introduced Spaced Repetition, a method that strengthens memory by reviewing material at expanding intervals. We also examined Deliberate Practice, which pushes learners just beyond their comfort zone to promote genuine skill development, and Bloom’s Taxonomy, which outlines the stages of cognitive growth from remembering to creating.</p>
<p>Finally, we emphasized the importance of knowing when to step back. The brain operates in both focused and diffuse modes, and effective learning requires movement between the two. Breaks are not signs of weakness but essential components of consolidation and insight.</p>
<p>Together, these theories form a comprehensive framework for learning to code with scientific precision. When self‑taught developers understand how their brain learns, forgets, and grows, they can design a learning process that isn't only more efficient but far more sustainable.</p>
<p>With all this new knowledge, one truth is certain: focus, determination, and consistency are the forces that transform theory into mastery.</p>
<p>Learning science can guide the process, but only sustained effort turns knowledge into skill.</p>
<h2 id="heading-references">References</h2>
<ol>
<li><p>Clearwater, L. (2024). <em>Understanding the Science Behind Learning Retention | Reports | What We Think | Indegene</em>. [online] <a href="http://www.indegene.com">www.indegene.com</a>. Available at: <a href="https://www.indegene.com/what-we-think/reports/understanding-science-behind-learning-retention">https://www.indegene.com/what-we-think/reports/understanding-science-behind-learning-retention</a>.</p>
</li>
<li><p>Dylan Wiliam [@dylanwiliam]. (2017, January 25). <em>I’ve come to the conclusion Sweller’s Cognitive Load Theory is the single most important thing for teachers to know</em> [Tweet]. X. <a href="https://x.com/dylanwiliam/status/824682504602943489">https://x.com/dylanwiliam/status/824682504602943489</a></p>
</li>
<li><p>Ericsson, K. A., Krampe, R. T., &amp; Tesch-Römer, C. (1993).<br><em>The role of deliberate practice in the acquisition of expert performance.</em><br><strong>Psychological Review, 100(3), 363–406.</strong></p>
</li>
<li><p>Garnett, S. (2020.). <em>Cognitive Load Theory A handbook for teachers</em>. [online] Available at: <a href="https://www.crownhouse.co.uk/assets/look-inside/9781785835018.pdf">https://www.crownhouse.co.uk/assets/look-inside/9781785835018.pdf</a>.</p>
</li>
<li><p>Geary, D. C. (2007). <em>An evolutionary perspective on learning disability in mathematics</em>. Developmental Neuropsychology, 32(1), 471–519. <a href="https://doi.org/10.1080/87565640701360924">https://doi.org/10.1080/87565640701360924</a></p>
</li>
<li><p>Geary, D. C. (2008). <em>An evolutionarily informed education science</em>. Educational Psychologist, 43(4), 179–195. <a href="https://doi.org/10.1080/00461520802392133">https://doi.org/10.1080/00461520802392133</a></p>
</li>
<li><p>George A. Miller (1956). <em>The magical number seven, plus or minus two: Some limits on our capacity for processing information</em>. Psychological Review, 63(2), 81–97. <a href="https://doi.org/10.1037/h0043158">https://doi.org/10.1037/h0043158</a></p>
</li>
<li><p>Nelson Cowan (2001). <em>The magical number 4 in short-term memory: A reconsideration of mental storage capacity</em>. Behavioral and Brain Sciences, 24(1), 87–114. <a href="https://doi.org/10.1017/S0140525X01003922">https://doi.org/10.1017/S0140525X01003922</a></p>
</li>
<li><p>Oakley, B. (2014). <em>A Mind for Numbers: How to Excel at Math and Science (Even If You Flunked Algebra).</em> New York: TarcherPerigee.</p>
</li>
<li><p>Sweller, J. (1988). Cognitive Load during Problem Solving: Effects on Learning. <em>Cognitive Science</em>, [online] 12(2), pp.257–285. doi:<a href="https://doi.org/10.1207/s15516709cog1202_4">https://doi.org/10.1207/s15516709cog1202_4</a>.</p>
</li>
</ol>
<p>‌</p>
<p>‌</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build and Deploy a Fitness Tracker Using Python Django and PythonAnywhere - A Beginner Friendly Guide ]]>
                </title>
                <description>
                    <![CDATA[ If you've learned some Python basics but still feel stuck when it comes to building something real, you're not alone. Many beginners go through tutorials, learn about variables, functions, and loops,  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-and-deploy-a-fitness-tracker-using-python-django-and-pythonanywhere/</link>
                <guid isPermaLink="false">69cfff6ce466e2b762506a84</guid>
                
                    <category>
                        <![CDATA[ Programming Blogs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Django ]]>
                    </category>
                
                    <category>
                        <![CDATA[ deployment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Prabodh Tuladhar ]]>
                </dc:creator>
                <pubDate>Fri, 03 Apr 2026 17:57:00 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/a1ae273b-9f92-4fc2-89aa-1452fc0df895.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you've learned some Python basics but still feel stuck when it comes to building something real, you're not alone. Many beginners go through tutorials, learn about variables, functions, and loops, and then hit a wall when they try to create an actual project.</p>
<p>The gap between "I know Python syntax" and "I can build a working web app" can feel enormous. But it does not have to be.</p>
<p>In this tutorial, you'll build a fitness tracker web application from scratch using Django, one of the most popular Python web frameworks. By the end, you'll have a fully functional app running live on the internet – something you can show to friends, add to your portfolio, or keep building on.</p>
<p>Here's what you'll learn:</p>
<ul>
<li><p>How Django projects and apps are structured</p>
</li>
<li><p>How to define database models to store workout data</p>
</li>
<li><p>How to create views that handle user requests</p>
</li>
<li><p>How to build HTML templates that display your data</p>
</li>
<li><p>How to connect URLs to views so users can navigate your app</p>
</li>
<li><p>How to deploy your finished app to PythonAnywhere so anyone can access it</p>
</li>
</ul>
<p>The app itself is straightforward: you can log a workout by entering an activity name, duration, and date. You can then view all your logged workouts on a separate page. It's simple, but it covers the core Django concepts you need to build much bigger things later.</p>
<p>Let's get started.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-what-you-are-going-build">What You Are Going Build</a></p>
</li>
<li><p><a href="#heading-step-1-how-to-set-up-your-django-project">Step 1: How to Set Up Your Django Project</a></p>
<ul>
<li><p><a href="#heading-1-1-how-to-create-a-virtual-environment">1. 1 How to create a virtual environment</a></p>
</li>
<li><p><a href="#heading-12-how-to-install-django">1.2 How to install Django</a></p>
</li>
<li><p><a href="#heading-13-how-to-create-the-project">1.3 How to Create the Project</a></p>
</li>
<li><p><a href="#heading-14-how-to-run-the-development-server">1.4 How to run the development server</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-2-how-to-create-a-django-app">Step 2: How to Create a Django App</a></p>
<ul>
<li><p><a href="#heading-21-how-to-generate-the-app">2.1 How to Generate the App</a></p>
</li>
<li><p><a href="#heading-22-how-to-register-the-app">2.2 How to Register the App</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-3-how-to-create-a-workout-model">Step 3: How to create a Workout Model</a></p>
<ul>
<li><a href="#heading-31-how-to-define-the-model">3.1 How to Define the Model</a></li>
</ul>
</li>
<li><p><a href="#heading-step-4-how-to-apply-migrations">Step 4: How to Apply Migrations</a></p>
<ul>
<li><p><a href="#heading-41-how-to-generate-the-migration">4.1 How to Generate the Migration</a></p>
</li>
<li><p><a href="#heading-42-how-to-apply-the-migration">4.2 How to Apply the Migration</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-5-how-to-register-the-model-in-the-admin-panel">Step 5: How to Register the Model in the Admin Panel</a></p>
<ul>
<li><p><a href="#heading-52-how-to-create-a-superuser">5.2 How to Create a Superuser</a></p>
</li>
<li><p><a href="#heading-53-how-to-access-the-admin-panel">5.3 How to Access the Admin Panel</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-6-how-to-create-views-for-the-app">Step 6: How to Create Views for the App</a></p>
<ul>
<li><p><a href="#heading-61-how-to-create-a-form-class">6.1 How to Create a Form Class</a></p>
</li>
<li><p><a href="#heading-62-how-to-write-views">6.2 How to Write Views</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-7-how-to-create-templates">Step 7: How to Create Templates</a></p>
<ul>
<li><p><a href="#heading-71-how-to-set-up-the-template-directory">7.1 How to Set Up the Template Directory</a></p>
</li>
<li><p><a href="#heading-72-how-to-create-the-workout-list-template">7.2 How to Create the Workout List Template</a></p>
</li>
<li><p><a href="#heading-73-how-to-create-add-workout-template">7.3 How to Create Add Workout Template</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-8-how-to-connect-urls">Step 8: How to Connect URLs</a></p>
<ul>
<li><p><a href="#heading-81-how-to-create-app-level-urls">8.1 How to Create App Level URLs</a></p>
</li>
<li><p><a href="#heading-82-how-to-link-app-urls-to-project">8.2 How to Link App URLs to project</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-step-9-how-to-test-the-application-locally">Step 9: How to Test the Application Locally</a></p>
</li>
<li><p><a href="#heading-step-10-how-to-prepare-for-deployment">Step 10: How to Prepare for Deployment</a></p>
<ul>
<li><a href="#heading-101-how-to-update-settings-for-production">10.1 How to Update Settings for Production</a></li>
</ul>
</li>
<li><p><a href="#heading-step-11-how-to-deploy-your-django-app-on-pythonanywhere">Step 11: How to Deploy Your Django App on PythonAnywhere</a></p>
<ul>
<li><p><a href="#heading-111-how-to-create-a-pythonanywhere-account">11.1 How to Create a PythonAnywhere Account</a></p>
</li>
<li><p><a href="#heading-112-how-to-upload-your-project-files">11.2 How to Upload Your Project Files</a></p>
</li>
<li><p><a href="#heading-113-how-to-set-up-a-virtual-environment-in-pythonanywhere">11.3 How to Set Up a Virtual Environment in PythonAnywhere</a></p>
</li>
<li><p><a href="#heading-114-how-to-run-migrations-and-create-a-superuser-on-pythonanywhere">11.4 How to Run Migrations and Create a SuperUser on PythonAnywhere</a></p>
</li>
<li><p><a href="#heading-114-how-to-configure-the-web-app-in-pythonanywhere">11.4 How to Configure the Web App in Pythonanywhere</a></p>
</li>
<li><p><a href="#heading-115-how-to-set-the-virtual-environment-path">11.5 How to Set the Virtual Environment Path</a></p>
</li>
<li><p><a href="#heading-116-how-to-configure-the-wsgi-file">11.6 How to Configure the WSGI file</a></p>
</li>
<li><p><a href="#heading-117-how-to-set-up-static-files">11.7 How to Set Up Static Files</a></p>
</li>
<li><p><a href="#heading-118-how-to-view-your-live-application">11.8 How to View Your Live Application</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-common-mistakes-and-how-to-fix-them">Common Mistakes and How to Fix Them</a></p>
</li>
<li><p><a href="#heading-how-you-can-improve-this-project">How You Can Improve This Project</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you begin, make sure you are comfortable with the following:</p>
<p><strong>Python fundamentals:</strong> You should understand variables, functions, lists, dictionaries, and basic control flow (if/else statements and loops).</p>
<p><strong>Basic command line usage:</strong> You'll be running commands in your terminal throughout this tutorial. You should know how to open a terminal, navigate between folders, and run commands. If you're on Windows, you can use Command Prompt or PowerShell. On macOS or Linux, the default Terminal app works well.</p>
<p><strong>Tools you'll need installed:</strong></p>
<ul>
<li><p><strong>Python 3.8 or higher.</strong> You can check your version by running <code>python --version</code> or <code>python3 --version</code> in your terminal.&nbsp; If you don't have Python installed, download it from <a href="https://www.python.org">python.org</a></p>
</li>
<li><p><strong>pip.</strong> This is Python's package manager. It usually comes bundled with Python. You can verify by running <code>pip --version</code> or pip3 --version. Note the commands <code>python3</code> and <code>pip3</code> tell the terminal that you are explicitly using <strong>Python Version 3</strong></p>
</li>
<li><p><strong>A code editor.</strong> Visual Studio Code is a great free option, but you can use any editor you're comfortable with.</p>
</li>
</ul>
<p>That's everything. You don't need prior Django experience or web development knowledge. This tutorial will walk you through each step.</p>
<h2 id="heading-what-you-are-going-build">What You Are Going Build</h2>
<p>The fitness tracker you will build has two main features:</p>
<ol>
<li><strong>A form to log workouts.</strong> You will enter the name of an activity (like "Running" or "Push-ups"), how long you did it (in minutes), and the date. When you submit the form, Django saves that workout to a database.</li>
</ol>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/fe6b2a89-fc29-4710-a640-ce2757267e38.png" alt="The image shows a form to log workouts" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<ol>
<li><strong>A page to view all your workouts.</strong> This page displays every workout you have logged, showing the activity, duration, and date in a clean list.</li>
</ol>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/8f6bd09e-497a-4480-83e5-af162028a0a3.png" alt="The image shows a list of logged workouts" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Here's how data flows through the app at a high level:</p>
<ol>
<li><p>You fill out the workout form in your browser and click submit.</p>
</li>
<li><p>Your browser sends that data to Django.</p>
</li>
<li><p>Django's view function receives the data, validates it, and saves it to the database.</p>
</li>
<li><p>When you visit the workouts page, Django's view function pulls all saved workouts from the database.</p>
</li>
<li><p>Django passes that data to an HTML template, which renders it as a page your browser can display.</p>
</li>
</ol>
<img alt="The image shows the data flow of the fitness tracker app with 5 steps" style="display:block;margin-left:auto" width="600" height="400" loading="lazy">

<p>This request-response cycle is the foundation of how Django works. Once you understand it, you can build almost anything.</p>
<h2 id="heading-step-1-how-to-set-up-your-django-project">Step 1: How to Set Up Your Django Project</h2>
<p>Every Django project starts with a few setup steps. You'll create an isolated Python environment, install Django, and generate the initial project structure.</p>
<h3 id="heading-1-1-how-to-create-a-virtual-environment">1. 1 How to Create a Virtual Environment</h3>
<p>A virtual environment is a self-contained folder that contains its own Python interpreter and installed packages for a specific project. This keeps your project's dependencies separate from other Python projects on your computer. This separation prevents version conflicts and keeps setups consistent.</p>
<p>For example, one project might require an older version of Django, while another needs the latest version, and a virtual environment allows both to work smoothly on the same system.</p>
<p>Without it, global installations can clash, break projects, and make setups hard to reproduce. Over time, the system environment becomes cluttered with unused or incompatible packages making debugging and maintenance more difficult.</p>
<p>Now let's set it up.</p>
<p>Open your terminal, and navigate to where you want your project to live and run the following command</p>
<pre><code class="language-shell">mkdir fitness-tracker
cd fitness-tracker
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/b4715e51-c2e3-4e97-ad7b-b41066aeefd9.png" alt="An image of the terminal showing the commands mkdir (make directory) and cd (change directory) being typed " style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>The first command creates a new folder called <code>fitness-tracker</code>. The second command moves you into that folder.</p>
<p>You'll create the Python virutal environment here.</p>
<pre><code class="language-shell">python3 -m venv venv
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/3d1723ae-d069-48fd-9954-440a191f585f.png" alt="The image shows the command to create the python virtual enviroment." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>The above command creates a virtual environment inside a folder called <code>venv</code>. The first <code>venv</code> is the command and the second <code>venv</code> represents the name of the folder. You can name the folder anything though <code>venv</code> is usually preferred.</p>
<p>By using the <code>ls</code> command, you can see that we've created the virtual environment folder.</p>
<p>To activate the virtual environment, we need to use the following command:</p>
<p>On macOS/Linux:</p>
<pre><code class="language-shell">source venv/bin/activate
</code></pre>
<p>On Windows:</p>
<pre><code class="language-shell">venv\Scripts\activate
</code></pre>
<p>You'll know it worked when you see <code>(venv)</code> at the beginning of your terminal prompt. From this point on, any Python packages you install will only exist inside this <strong>virtual environment</strong>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/dabe362d-2f50-4745-a0bc-e57ad3536723.png" alt="The image shows the virtual environment being activated" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-12-how-to-install-django">1.2 How to Install Django</h3>
<p>With your virtual environment activated, install Django using pip:</p>
<pre><code class="language-shell">pip install django
</code></pre>
<p>This downloads and installs the latest stable version of Django. You can verify the installation by running:</p>
<pre><code class="language-shell">python3 -m django --version
</code></pre>
<p>After running both these commands, you should see Django being installed and the version number:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/bda2ed0d-44bf-439b-a9fd-1cf3fcaf35ca.png" alt="The image shows django being installed and the version of django that has been installed" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-13-how-to-create-the-project">1.3 How to Create the Project</h3>
<p>We have finished installing Django. Now let's create a Django project. Django provides a command line utility that generates the boilerplate files that you need. Type the following command:</p>
<pre><code class="language-shell">django-admin startproject fitness_project .
</code></pre>
<p>The command creates a folder named <code>fitness-project</code>. Notice the dot at the end of the command. The dot at the end is important. It tells Django to create the project files in your current directory instead of creating an extra nested folder.</p>
<p>Now that we've created our Django project, let's open the project in your favourite text editor and look at folder structure.</p>
<p>You'll notice that the folder already comes with a bunch of files.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/eaffe95e-7078-4c2e-91f4-88a6c3696e88.png" alt="The image show the list of files created by the django-admin startproject command" width="600" height="400" loading="lazy">

<h3 id="heading-14-how-to-run-the-development-server">1.4 How to Run the Development Server</h3>
<p>Now let's make sure everything is working. You'll need to run a server for this. Type the following command:</p>
<pre><code class="language-shell">python manage.py runserver
</code></pre>
<p>You can type this command in the terminal with the virtual environment activated or you can use the integrated terminal if you're using VS Code. I'll be using the integrated terminal from this point on.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/53dbba38-9863-4972-a899-1e6ff66fb3f5.png" alt="This is an image of the server running after typing the runserver command" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Open your browser and go to <a href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a>. You should see Django's default welcome page with a rocket ship graphic confirming that your project is set up correctly.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/25d2e3ce-ce72-44f6-9f5f-78aeaeb88b3e.png" alt="This is an image of Django's default homepage" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Press <code>Ctrl + C</code> in your terminal to stop the server when you're ready to move on.</p>
<h2 id="heading-step-2-how-to-create-a-django-app">Step 2: How to Create a Django App</h2>
<p>In Django, a project is the overall container for your entire web application, while an app is a smaller, self-contained module inside that project that focuses on a specific piece of functionality.</p>
<p>A useful way to picture this is to think of a house. The project is the whole house. Each app is like a room inside that house. One room might be a kitchen, another a bedroom, each designed with a clear purpose. In the same way, a Django app is built to handle one responsibility, such as authentication, payments, or in this case, workout tracking.</p>
<p>Now, here's the important part: why not just put everything into one big project instead of using apps? You technically could, especially for very small projects. But as your application grows, that approach quickly becomes difficult to manage.</p>
<p>By using apps, you naturally separate concerns. It also makes collaboration smoother, since different people can work on different apps without constantly stepping on each other’s code.</p>
<p>Another major benefit is reusability. Since apps are modular, you can take an app from one project and reuse it in another.</p>
<p>For example, if you build a workout tracking app once, you could plug it into a completely different Django project later without rebuilding it from scratch. Later, you might create a completely different project, say a fitness coaching platform or a health dashboard. Instead of rebuilding the tracking feature from scratch, you can reuse the same app.</p>
<p>For this project, you'll create a single app called <code>tracker</code> that handles everything related to logging and displaying workouts.</p>
<h3 id="heading-21-how-to-generate-the-app">2.1 How to Generate the App</h3>
<p>Make sure you're in the same directory as the <code>manage.py</code> file, then run the following code:</p>
<pre><code class="language-shell">python manage.py startapp tracker
</code></pre>
<p>This create a new folder called tracker with the following following structure:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/cb07105a-6e65-49f9-9c7a-d5a64db42b49.png" alt="The image shows the folder strucutre created by after running the startapp command" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Each file has its own purpose. You'll work with <code>models.py</code>, <code>views.py</code> and <code>admin.py</code> throughout this project.</p>
<h3 id="heading-22-how-to-register-the-app">2.2 How to Register the App</h3>
<p>Django doesn't automatically know about your new app. You need to tell it by adding the app to the <code>INSTALLED_APPS</code> list in <code>settings.py</code> file.</p>
<p>Open <code>fitness_project/settings.py</code> and find the <code>INSTALLED_APPS</code> list. Add the name of the app, that is <code>tracker</code>, to the end of the list:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/eec90a01-5219-449e-97f8-97465e4ac23f.png" alt="eec90a01-5219-449e-97f8-97465e4ac23f" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>You'll notice that a number of apps have already been installed automatically by Django. This is part of Django’s “batteries-included” philosophy, where many common features are ready to use out of the box.</p>
<p>Here is a short summary of what each of the apps does.</p>
<table>
<thead>
<tr>
<th><strong>App Name</strong></th>
<th><strong>Purpose</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>django.contrib.admin</strong></td>
<td>Powers the built-in admin dashboard, letting you manage your data through a web interface.</td>
</tr>
<tr>
<td><strong>django.contrib.auth</strong></td>
<td>Handles users, login systems, permissions, and password management.</td>
</tr>
<tr>
<td><strong>django.contrib.contenttypes</strong></td>
<td>Helps Django track and manage relationships between different models.</td>
</tr>
<tr>
<td><strong>django.contrib.sessions</strong></td>
<td>Stores user session data, so users stay logged in across requests.</td>
</tr>
<tr>
<td><strong>django.contrib.messages</strong></td>
<td>Lets you show temporary notifications like success or error messages.</td>
</tr>
<tr>
<td><strong>django.contrib.staticfiles</strong></td>
<td>Manages static assets such as CSS, JavaScript, and images</td>
</tr>
</tbody></table>
<p>Now Django knows your <code>tracker</code> app exists and will include it when running the project.</p>
<h2 id="heading-step-3-how-to-create-a-workout-model">Step 3: How to Create a Workout Model</h2>
<p>A model in Django is a Python class that defines the structure of your data. Each model maps directly to a table in your database. Each attribute on the model becomes a column in that table.</p>
<p>Think of a model as a blueprint for a spreadsheet. The class name is the name of the spreadsheet, and each field is a column header. Every time you save a new workout, Django creates a new row in that spreadsheet.</p>
<h3 id="heading-31-how-to-define-the-model">3.1 How to Define the Model</h3>
<p>Open <code>tracker/models.py</code> and replace its contents with this code:</p>
<pre><code class="language-python">from django.db import models

class Workout(models.Model):
    activity = models.CharField(max_length=200)
    duration = models.IntegerField(help_text="Duration in minutes")
    date = models.DateField()

    def __str__(self):
        return f"{self.activity} - {self.duration} min on {self.date}"
</code></pre>
<p>Let's discuss what each part does:</p>
<ul>
<li><p><code>activity = models.CharField(max_length=200)</code> creates a text fields that can hold up to 200 characters. This is where you'll store the name of the exercise like "Running" or "Cycling".</p>
</li>
<li><p><code>duration = models.IntegerField(help_text="Duration in minutes")</code> creates a whole number field for storing how many minutes the workout lasted. The <code>help_text</code> parameter adds a hint that will appear in forms and the admin panel.</p>
</li>
<li><p><code>date = models.DateField()</code> creates a date field for recording when the workout happened.</p>
</li>
</ul>
<p>The <code>__str__()</code> method defines how a Workout object appears when printed or displayed in the admin panel. Instead of seeing something unhelpful like "<strong>Workout object (1)</strong>," you will see "<strong>Running - 30 min on 2025-03-15.</strong>"</p>
<h2 id="heading-step-4-how-to-apply-migrations">Step 4: How to Apply Migrations</h2>
<p>You've defined your model, but Django hasn't created the actual database table yet. To do that, you need to run migrations.</p>
<p>Migrations are Django's way of translating your Python model definitions into database instructions. Migrations are done in two steps.</p>
<p>When you change a model – maybe by adding a field, removing a field, or renaming one – you create a new migration that describes that change. You can do this using the <code>makemigrations</code> command.</p>
<p>Then you apply the migration using the <code>migrate</code> command and Django updates the database to match.</p>
<p>This two-step process of first detecting the change and then applying the change gives you a reliable record of every change to your database structure over time.</p>
<h3 id="heading-41-how-to-generate-the-migration">4.1 How to Generate the Migration</h3>
<p>Run the following command in the integrated terminal:</p>
<pre><code class="language-shell">python manage.py makemigrations
</code></pre>
<p>You should see output like this:</p>
<pre><code class="language-shell">Migrations for 'tracker': tracker/migrations/0001_initial.py 
    + Create model Workout
</code></pre>
<p>Django inspected your Workout model and created a migration file that describes how to build the corresponding database table. You can find this file at <code>tracker/migrations/0001_initial.py</code> if you want to look at it, but you don't need to edit it.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/fa46eed5-6ef3-408a-8c23-f39518b117f4.png" alt="The image shows the file creating after makemigrations command runs" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-42-how-to-apply-the-migration">4.2 How to Apply the Migration</h3>
<p>Now tell Django to execute that migration and actually create the table in the database:</p>
<pre><code class="language-shell">python manage.py migrate
</code></pre>
<p>You'll see several lines of output as Django applies not just your migration, but also the default migrations for Django's built-in apps (authentication, sessions, and so on).</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/fcaae5fe-0cc7-4c1f-b4c3-a3b173fd2551.png" alt="The image shows the output after applying migrations" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>When it finishes, your database has a table ready to store workouts.</p>
<p>When the migrate command runs, we can see the exact SQL commands that Django used to build and change the database. Though this isn't required for creating the application, it's always good to know what's happening under hood.</p>
<p>Run this command:</p>
<pre><code class="language-shell">python manage.py sqlmigrate tracker 001
</code></pre>
<p>And you should get this output:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/016b0a33-06d0-47e8-97de-580e79a7d0e3.png" alt="The image shows the command to view sql queries created by django" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>The <code>001</code> you added at the end is the migration number and represents first version of the database schema.</p>
<p>In practice, your workflow usually looks like this: you change your models, run <code>makemigrations</code> to generate the migration files, and then run the <code>migrate</code> command to apply those changes to the database.</p>
<h2 id="heading-step-5-how-to-register-the-model-in-the-admin-panel">Step 5: How to Register the Model in the Admin Panel</h2>
<p>Django comes with a powerful admin interface built in. It gives you a graphical way to view, add, edit, and delete records in your database without writing any extra code. This is incredibly useful during development because you can quickly test your models and see your data.</p>
<p>But by default, it doesn’t know:</p>
<ul>
<li><p>Which models you want to manage</p>
</li>
<li><p>How you want them displayed</p>
</li>
</ul>
<p>So you <em>register</em> models in <code>admin.py</code> to tell Django to include the specific model in the admin interface.</p>
<h3 id="heading-51-how-to-add-model-to-admin">5.1 How to Add Model to Admin</h3>
<p>Open <code>tracker/admin.py</code> and add the following code:</p>
<pre><code class="language-python">from django.contrib import admin
from .models import Workout

admin.site.register(Workout)
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/ad017508-993a-4c28-ade1-8e73fa0c6a4a.png" alt="ad017508-993a-4c28-ade1-8e73fa0c6a4a" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>This single line tells Django to include the <code>Workout</code> model in the admin interface.</p>
<h3 id="heading-52-how-to-create-a-superuser">5.2 How to Create a Superuser</h3>
<p>To access the admin panel, you need an admin account. Create one by running:</p>
<pre><code class="language-python">python manage.py createsuperuser
</code></pre>
<p>Django will prompt you for a username, email address, and password. Choose something you will remember. The email is optional – you can press Enter to skip it.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/4bbc7a15-682e-497d-a4a4-3e2dc4b848ac.png" alt="The image shows the superuser being created by adding username, email and password" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-53-how-to-access-the-admin-panel">5.3 How to Access the Admin Panel</h3>
<p>Start the development server:</p>
<pre><code class="language-python">python manage.py runserver
</code></pre>
<p>Then navigate to <a href="http://127.0.0.1:8000/admin/">http://127.0.0.1:8000/admin/</a> in your browser. Log in with the credentials you just created.</p>
<p>You should see the Django administration dashboard with a "<strong>Tracker</strong>" section containing your "<strong>Workouts</strong>" model.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/a1e576bf-45f6-40dc-b6b9-69899b2df9d5.png" alt="The image shows the Django admin panel and the Worker model of the Tracker app being added to the admin panel" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Try clicking "Add" to create a couple of test workouts. This will confirm that your model is working correctly before you build the rest of the app.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/944ea6a4-bc6f-4321-87c0-5c7bcb267e26.png" alt="The image show some workouts (running and cycling) being added to the admin panel" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h2 id="heading-step-6-how-to-create-views-for-the-app">Step 6: How to Create Views for the App</h2>
<p>A view in Django is a Python function (or class) that receives a web request and returns a web response. That response could be an HTML page, a redirect, a 404 error, or anything else a browser can handle.</p>
<p>Views are where your application logic lives. They decide what data to fetch, what processing to do, and what to show the user.</p>
<p>For this app, you need two views: one to display the form where users add a workout, and one to display the list of all saved workouts.</p>
<h3 id="heading-61-how-to-create-a-form-class">6.1 How to Create a Form Class</h3>
<p>Before writing the views, you need a Django form that handles the workout input.</p>
<p>Django forms are a built-in way to handle user input like login forms, contact forms, or anything that collects data from a user. Instead of manually writing HTML, validating inputs, and handling errors, Django gives you a structured way to do all of that in one place.</p>
<p>Most user inputs are based on the models you’ve created, and Django can automatically generate forms from those models using <code>ModelForms</code>, which speeds things up significantly.</p>
<p>Let's create a new file called <code>forms.py</code> in the <code>tracker</code> folder and add the following code:</p>
<pre><code class="language-python">from django import forms
from .models import Workout

class WorkoutForm(forms.ModelForm):

    class Meta:
        model = Workout
        fields = ['activity', 'duration', 'date']
        widgets = {
            'date': forms.DateInput(attrs={'type': 'date'}),
        }
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/e8bce19c-7184-45b3-9afe-3a5f73cff43b.png" alt="The image shows the file location of forms.py as well the code for forms.py file" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>In the above code, the <code>ModelForm</code> automatically generates form fields based on the <code>Workout</code> model. The <code>widgets</code> dictionary tells Django to render the date field as an HTML date picker instead of a plain text input.</p>
<p>We can actually see the forms being automatically created by Django. For this we need to enter the shell. In the terminal, type the following command:</p>
<pre><code class="language-shell">python manage.py shell
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/951829fb-98e8-48f5-8187-80cc98346e06.png" alt="The image shows the python shell being activated" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Now lets import the <code>WorkoutForm</code> class that we just created.</p>
<p>Type the following code:</p>
<pre><code class="language-shell">from tracker.forms import WorkoutForm
</code></pre>
<p>Notice that we've given the <strong>name of the app</strong> as well when we imported the form.</p>
<p>Then create an object of the <code>WorkoutForm</code> class and print it.</p>
<pre><code class="language-shell">from tracker.forms import WorkoutForm
workoutform = WorkoutForm()
print(workoutform) 
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/b81265a2-7c21-453b-ae57-3cfec97fbaf9.png" alt="The image shows the command to open the python shell where you can execute python statement throught the terminal" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>You should get the following output:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/094b0d89-b003-49b6-939d-07eacfb0c745.png" alt="This image shows the html generated from ModelForm" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>You can see that all the model fields have been renderd as HTML forms and the date field has been created as a date type that is <code>type="date"</code> instead of plain text.</p>
<h3 id="heading-62-how-to-write-views">6.2 How to Write Views</h3>
<p>As we've discussed above, our project has two views: one to add a workout and the other to display all the saved workouts.</p>
<p>First, let's create a view to add a workout. In the <code>tracker/views.py</code> file, type the following code:</p>
<pre><code class="language-python">from django.shortcuts import render, redirect
from .models import Workout

# view to list all workouts
def workout_list(request):
    workouts = Workout.objects.all().order_by('-date')
    return render(request, 'tracker/workout_list.html', {'workouts': workouts})
</code></pre>
<p>Let's walk through this view:</p>
<ul>
<li><p>The <code>workout_list</code> view handles the page that displays all workouts.</p>
</li>
<li><p>It queries the database for every <code>Workout</code> object, orders them by date (most recent first, thanks to the <code>-</code> prefix), and passes that list to a template called <code>workout_list.html</code>.</p>
</li>
<li><p>The <code>render</code> function combines the template with the data and returns the finished HTML page.</p>
</li>
</ul>
<p>To create the logic to add a workout, first add the <code>Workout</code> form import at the end of the import section. Then add the following code after the <code>workout_list</code> view:</p>
<pre><code class="language-python">from django.shortcuts import render, redirect
from .models import Workout
from .forms import WorkoutForm

# view to list all the workouts
def workout_list(request):
    workouts = Workout.objects.all().order_by('-date')
    return render(request, 'tracker/workout_list.html', {'workouts': workouts})

# view to add a workout
def add_workout(request):
    if request.method == 'POST':
        form = WorkoutForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('workout_list')
    else:
        form = WorkoutForm()
    return render(request, 'tracker/add_workout.html', {'form': form})
</code></pre>
<ul>
<li><p>The <code>add_workout</code> view handles both displaying the empty form and processing submitted form data.</p>
</li>
<li><p>When a user first visits the page, the request method is GET, so Django creates a blank form and renders it.</p>
</li>
<li><p>When the user fills out the form and clicks submit, the request method is POST. Django then validates the submitted data, saves it to the database if everything is correct, and redirects the user to the workout list page.</p>
</li>
<li><p>If the data isn't valid, Django re-renders the form with error messages.</p>
</li>
</ul>
<p>Here is the complete views code:</p>
<pre><code class="language-python">from django.shortcuts import render, redirect
from .models import Workout
from .forms import WorkoutForm

# view to list all workouts
def workout_list(request):
    workouts = Workout.objects.all().order_by('-date')
    return render(request, 'tracker/workout_list.html', {'workouts': workouts})

# view to add a workout
def add_workout(request):
    if request.method == 'POST':
        form = WorkoutForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('workout_list')
    else:
        form = WorkoutForm()
    return render(request, 'tracker/add_workout.html', {'form': form})

</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/8a0878c1-f029-49a4-8a7f-308cdb843b62.png" alt="The image shows the complete code for views.py with explanation about the add workout view" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h2 id="heading-step-7-how-to-create-templates">Step 7: How to Create Templates</h2>
<p>Templates are HTML files that Django fills in with dynamic data. They're the front end of your application: the part users actually see in their browser.</p>
<h3 id="heading-71-how-to-set-up-the-template-directory">7.1 How to Set Up the Template Directory</h3>
<p>Django looks for templates inside a <code>templates</code> folder within each app. Create the following folder structure inside your <code>tracker</code> app.</p>
<p><code>tracker/templates/tracker</code></p>
<p>The double <code>tracker</code> folder name might look redundant, but it's a Django convention called <strong>template namespacing</strong>. It prevents naming conflicts if you have multiple apps with templates that share the same filename.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/cfbe85c3-36dc-413e-918a-aa64d706d2fc.png" alt="The image shows folder structure of the templates folder" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-72-how-to-create-the-workout-list-template">7.2 How to Create the Workout List Template</h3>
<p>Create a file called <code>tracker/templates/tracker/workout_list.html</code> and add the following code:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;My Workouts&lt;/title&gt;
    &lt;style&gt;
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background-color: #f5f7fa;
            color: #333;
            line-height: 1.6;
            padding: 2rem;
        }

        .container {
            max-width: 700px;
            margin: 0 auto;
        }

        h1 {
            font-size: 1.8rem;
            margin-bottom: 1rem;
            color: #1a1a2e;
        }

        .add-link {
            display: inline-block;
            background-color: #4361ee;
            color: white;
            padding: 0.6rem 1.2rem;
            border-radius: 6px;
            text-decoration: none;
            margin-bottom: 1.5rem;
            font-size: 0.95rem;
        }

        .add-link:hover {
            background-color: #3a56d4;

        }

        .workout-card {
            background: white;
            border-radius: 8px;
            padding: 1rem 1.2rem;
            margin-bottom: 0.8rem;
            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
            display: flex;
            justify-content: space-between;
            align-items: center;

        }

        .workout-activity {
            font-weight: 600;
            font-size: 1.05rem;

        }

        .workout-details {
            color: #666;
            font-size: 0.9rem;

        }

        .empty-state {
            text-align: center;
            padding: 3rem 1rem;
            color: #888;

        }

    &lt;/style&gt;
&lt;/head&gt;

&lt;body&gt;
    &lt;div class="container"&gt;
        &lt;h1&gt;My Workouts&lt;/h1&gt;
        &lt;a href="{% url 'add_workout' %}" class="add-link"&gt;+ Log a Workout&lt;/a&gt;
        {% if workouts %}
            {% for workout in workouts %}
                &lt;div class="workout-card"&gt;
                    &lt;div&gt;
                        &lt;div class="workout-activity"&gt;{{ workout.activity }}&lt;/div&gt;
                        &lt;div class="workout-details"&gt;{{ workout.duration }} minutes&lt;/div&gt;
                    &lt;/div&gt;
                    &lt;div class="workout-details"&gt;{{ workout.date }}&lt;/div&gt;
                &lt;/div&gt;
            {% endfor %}

        {% else %}
            &lt;div class="empty-state"&gt;
                &lt;p&gt;No workouts logged yet. Start by adding one!&lt;/p&gt;
            &lt;/div&gt;
        {% endif %}
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>There are a few things worth noting here:</p>
<p>If you look closely at the HTML, you'll spot some weird-looking tags wrapped in curly braces ( <code>{% %}</code> and <code>{{ }}</code> ). Think of them as special instructions for Django.</p>
<p>You use the double curly braces (<code>{{ }}</code>) when you want to output or display a piece of data directly on the page.</p>
<p>On the other hand, you use the brace-and-percent-sign combo ( <code>{% %}</code> ) when you need Django to actually perform an action or apply logic, like running a loop or checking a condition.</p>
<p>They allow us to inject dynamic data straight from our Python backend right into our otherwise static HTML.</p>
<p>Lets look at this code snippet for the <code>workout_list.html</code></p>
<pre><code class="language-html">&lt;body&gt;
    &lt;div class="container"&gt;
        &lt;h1&gt;My Workouts&lt;/h1&gt;
        &lt;a href="{% url 'add_workout' %}" class="add-link"&gt;+ Log a Workout&lt;/a&gt;
        {% if workouts %}
            {% for workout in workouts %}
                &lt;div class="workout-card"&gt;
                    &lt;div&gt;
                        &lt;div class="workout-activity"&gt;{{ workout.activity }}&lt;/div&gt;
                        &lt;div class="workout-details"&gt;{{ workout.duration }} minutes&lt;/div&gt;
                    &lt;/div&gt;
                    &lt;div class="workout-details"&gt;{{ workout.date }}&lt;/div&gt;
                &lt;/div&gt;
            {% endfor %}

        {% else %}
            &lt;div class="empty-state"&gt;
                &lt;p&gt;No workouts logged yet. Start by adding one!&lt;/p&gt;
            &lt;/div&gt;
        {% endif %}
    &lt;/div&gt;
&lt;/body&gt;
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/864ed3dc-ceda-44f8-ba3d-b061222714c7.png" alt="The image shows the the body section of the workout_list.html with the focus on django template tags" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>There are a few things worth noting here.</p>
<p>Right under the main heading, you'll see this line:<br><code>&lt;a href="{% url 'add_workout' %}"&gt;</code></p>
<p>Instead of hardcoding a web link like <code>href="/add-workout/"</code>, Django uses the <code>{% url %}</code> tag to generate the link dynamically. You pass it the name of the route (in this case, <code>add_workout</code>), and Django automatically figures out the correct URL path.</p>
<p>If you ever change the URL structure in your Python code later, Django updates this link automatically. You never have to hunt through HTML files to fix broken links!</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/2dc56430-d146-4fc9-8ae5-a3fb5a0d7dd1.png" alt="The image highlights the code that generates dynamic url" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>The <code>{% if workouts %}</code> block checks whether there are any workouts to display. If the list is empty, it shows a friendly message instead of a blank page.</p>
<p>The <code>{% for workout in workouts %}</code> loop iterates over every workout in the list and renders a card for each one. The double curly braces <code>{{ workout.activity }}</code> insert the value of each field into the HTML</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/f75b5c7e-5cd0-457c-901f-e489c26a8175.png" alt="f75b5c7e-5cd0-457c-901f-e489c26a8175" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Inside the loop, you'll notice tags that look like this:</p>
<ul>
<li><p><code>{{ workout.activity }}</code></p>
</li>
<li><p><code>{{ workout.duration }}</code></p>
</li>
<li><p><code>{{ workout.date }}</code></p>
</li>
</ul>
<p>As Django loops through each workout object, it uses dot notation to peek inside that specific object and grab its details. It grabs the activity type (like "Running"), the duration ("30"), and the date ("March 30"), and prints that exact text directly onto the webpage for the user to see.</p>
<h3 id="heading-73-how-to-create-add-workout-template">7.3 How to Create Add Workout Template</h3>
<p>Create a file called <code>tracker/templates/tracker/add_workout.html</code> and add the following code:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;Log a Workout&lt;/title&gt;
    &lt;style&gt;
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;

        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background-color: #f5f7fa;
            color: #333;
            line-height: 1.6;
            padding: 2rem;
        }

        .container {
            max-width: 500px;
            margin: 0 auto;

        }

        h1 {
            font-size: 1.8rem;
            margin-bottom: 1.5rem;
            color: #1a1a2e;
        }

        .form-group {
            margin-bottom: 1.2rem;
        }

        label {
            display: block;
            margin-bottom: 0.3rem;
            font-weight: 600;
            font-size: 0.95rem;

        }

        input[type="text"],
        input[type="number"],
        input[type="date"] {
            width: 100%;
            padding: 0.6rem 0.8rem;
            border: 1px solid #ddd;
            border-radius: 6px;
            font-size: 1rem;
            transition: border-color 0.2s;
        }

        input:focus {
            outline: none;
            border-color: #4361ee;

        }

        .btn {
            background-color: #4361ee;
            color: white;
            padding: 0.7rem 1.5rem;
            border: none;
            border-radius: 6px;
            font-size: 1rem;
            cursor: pointer;
            margin-right: 0.5rem;
        }

        .btn:hover {
            background-color: #3a56d4;
        }

        .back-link {
            color: #4361ee;
            text-decoration: none;
            font-size: 0.95rem;
        }

        .back-link:hover {
            text-decoration: underline;
        }

        .actions {
            display: flex;
            align-items: center;
            gap: 1rem;
            margin-top: 0.5rem;
        }

        .error-list {
            color: #e74c3c;
            font-size: 0.85rem;
            margin-top: 0.3rem;

        }

    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div class="container"&gt;
       &lt;h1&gt;Log a Workout&lt;/h1&gt;
        &lt;form method="post"&gt;
            {% csrf_token %}
            &lt;div class="form-group"&gt;
                &lt;label for="id_activity"&gt;Activity&lt;/label&gt;
                {{ form.activity }}
                {% if form.activity.errors %}
                    &lt;div class="error-list"&gt;{{ form.activity.errors }}&lt;/div&gt;
                {% endif %}
            &lt;/div&gt;
            &lt;div class="form-group"&gt;
                &lt;label for="id_duration"&gt;Duration (minutes)&lt;/label&gt;
                {{ form.duration }}
                {% if form.duration.errors %}
                    &lt;div class="error-list"&gt;{{ form.duration.errors }}&lt;/div&gt;
                {% endif %}
            &lt;/div&gt;

            &lt;div class="form-group"&gt;
                &lt;label for="id_date"&gt;Date&lt;/label&gt;
                {{ form.date }}
                {% if form.date.errors %}
                    &lt;div class="error-list"&gt;{{ form.date.errors }}&lt;/div&gt;
                {% endif %}
            &lt;/div&gt;

            &lt;div class="actions"&gt;
                &lt;button type="submit" class="btn"&gt;Save Workout&lt;/button&gt;
                &lt;a href="{% url 'workout_list' %}" class="back-link"&gt;Cancel&lt;/a&gt;
            &lt;/div&gt;
        &lt;/form&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>In the previous template, we learned how to display data. Now, we're looking at a form that actually collects data. Handling forms manually in web development can get messy, but Django provides some powerful template tags to do the heavy lifting for us.</p>
<p>Let's look at the Django-specific logic powering this form:</p>
<p>First, right after opening the <code>&lt;form&gt;</code> tag, you'll spot a very important line: <code>{% csrf_token %}</code>. Whenever you submit data to a server using a "POST" method, malicious sites can potentially intercept or forge that request.</p>
<p>By including this <code>{% csrf_token %}</code>, you tell Django to generate a unique, hidden security key for the form. When the user clicks "Save Workout," Django checks this token to guarantee the request is legitimate. <strong>If you forget this tag, Django will simply reject your form!</strong></p>
<pre><code class="language-html">&lt;form method="post"&gt;
            {% csrf_token %}
            &lt;div class="form-group"&gt;
                &lt;label for="id_activity"&gt;Activity&lt;/label&gt;
                {{ form.activity }}
                {% if form.activity.errors %}
                    &lt;div class="error-list"&gt;{{ form.activity.errors }}&lt;/div&gt;
                {% endif %}
            &lt;/div&gt;
            &lt;div class="form-group"&gt;
                &lt;label for="id_duration"&gt;Duration (minutes)&lt;/label&gt;
                {{ form.duration }}
                {% if form.duration.errors %}
                    &lt;div class="error-list"&gt;{{ form.duration.errors }}&lt;/div&gt;
                {% endif %}
            &lt;/div&gt;

            &lt;div class="form-group"&gt;
                &lt;label for="id_date"&gt;Date&lt;/label&gt;
                {{ form.date }}
                {% if form.date.errors %}
                    &lt;div class="error-list"&gt;{{ form.date.errors }}&lt;/div&gt;
                {% endif %}
            &lt;/div&gt;

            &lt;div class="actions"&gt;
                &lt;button type="submit" class="btn"&gt;Save Workout&lt;/button&gt;
                &lt;a href="{% url 'workout_list' %}" class="back-link"&gt;Cancel&lt;/a&gt;
            &lt;/div&gt;
        &lt;/form&gt;
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/4ca2a034-119c-4dc7-a14a-cd0a81815c58.png" alt="The image shows a screenshot of the code and highlight the csrf token tag" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Now let's talk about automatically generating the form fields. Instead of manually typing out all the HTML <code>&lt;input&gt;</code> tags for the activity, duration, and date, we let Django do it for us using display tags (<code>{{ }}</code>).</p>
<p>Each <code>{{ form.activity }}</code>, <code>{{ form.duration }}</code>, and <code>{{ form.date }}</code> tag renders the corresponding form input. Django handles the HTML attributes, input types, and validation for you based on the model and form definitions.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/c2403685-b78d-4aa7-876d-63ca53481e37.png" alt="This image shows the code that automatically generates HTML forms" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>The error blocks below each field display validation messages if a user submits invalid data, like entering text in the duration field instead of a number. Users make mistakes. They might leave a required field blank or type text into a number field. Fortunately, Django validates the data for you and sends back errors if something goes wrong.</p>
<p>Underneath each input field, we use a logic block that looks like this:<br><code>{% if form.activity.errors %}</code></p>
<p>This code checks a simple condition: Did the user mess up this specific field? If Django found an error with the "activity" input, the code drops into the if block and uses<code>{{ form.activity.errors }}</code> block to print the exact error message (like "<strong>This field is required</strong>") right below the input box.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/97a61f9d-faf6-4ddd-ab17-d53da88e0d07.png" alt="This image displays the error blocks" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>You may notice that both templates include inline CSS rather than a separate stylesheet. For a small project like this, inline styles keep things simple and self-contained. In a larger project, you would use Django's static files system to manage CSS separately.</p>
<h2 id="heading-step-8-how-to-connect-urls">Step 8: How to Connect URLs</h2>
<p>You have views and templates, but Django doesn't know when to use them yet. You need to map URLs to views so that visiting a specific address in the browser triggers the right view function.</p>
<h3 id="heading-81-how-to-create-app-level-urls">8.1 How to Create App Level URLs</h3>
<p>Create a new file called <code>tracker/urls.py</code> and add the following code:</p>
<pre><code class="language-python">from django.urls import path
from . import views

urlpatterns = [ 
    path('', views.workout_list, name='workout_list'), 
    path('add/', views.add_workout, name='add_workout'), 
]
</code></pre>
<p>Each path function takes three arguments.</p>
<p>The first is the route string that represents a URL pattern (an empty string means the root of the app).</p>
<p>The second is the view function to call when that URL is visited.</p>
<p>The third is a name you can use to reference this URL elsewhere in your code, like in the <code>{% url %}</code> template tags you used earlier.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/d9128270-eec3-43af-93de-08780b4a53f5.png" alt="The image contains the description of three arguments of the path function" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-82-how-to-link-app-urls-to-project">8.2 How to Link App URLs to project</h3>
<p>Now that your app-level URLs are set up, the next step is to connect them to the main project so Django knows where to start routing requests. Think of it like linking a smaller map (your app) to a bigger map (your project), so everything works together smoothly.</p>
<p>Open <code>fitness_project.urls.py</code> and update it to include your app's URLs:</p>
<pre><code class="language-python">from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('tracker.urls')),
]
</code></pre>
<p>The <code>include()</code> function tells Django to look at the URL patterns defined in the <code>tracker/urls.py</code> file whenever someone visits your site. The empty string prefix means your tracker app handles requests at the root of the site.</p>
<p>Here's the full picture of how a request flows through the URL system.</p>
<p>When someone visits <a href="http://127.0.0.1:8000/add/">http://127.0.0.1:8000/add/</a>, Django first checks <code>fitness_project/urls.py</code>. It matches the empty prefix and delegates to <code>tracker/urls.py</code>. There, it matches <code>add/</code> and calls the <code>add_workout view</code>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/4590c4e3-0d32-4865-90db-a6504ee508c1.png" alt="The image shows the how the URL flows through the system" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h2 id="heading-step-9-how-to-test-the-application-locally">Step 9: How to Test the Application Locally</h2>
<p>At this point, your app has everything it needs to work. Let's test it.</p>
<p>Start the development server by running the command:</p>
<pre><code class="language-shell">python manage.py runserver
</code></pre>
<p>Open your browser and visit <a href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a>. You should see the workout list page with the heading "<strong>My Workouts</strong>" and a button that says "<strong>+ Log a Workout</strong>."</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/e15dc4cf-09b0-4ba9-95b1-baba32ce929f.png" alt="The image shows the My Workouts image with the button to log a workout" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Click that button. You should see the workout form with fields for activity, duration, and date.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/d9790d3e-0b6a-4e1b-b3d9-f305bc254cfc.png" alt="The image shows an empty form to log a workout" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Fill in some test data:</p>
<ul>
<li><p>Activity: Skipping</p>
</li>
<li><p>Duration: 25</p>
</li>
<li><p>Date: Pick today's date from the date picker</p>
</li>
</ul>
<p>Click "<strong>Save Workout</strong>" You should be redirected back to the workout list page, and your new workout should appear as a card.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/3f1dbf8f-3547-45ec-961d-cff75092ec02.png" alt="The image shows the workout list after adding a new workout" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Try adding a few more workouts with different activities and dates. Make sure they all show up on the list page in the correct order (most recent first).</p>
<p>This is also a good time to experiment. Try submitting the form with missing fields and see how Django handles validation.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/542e32cf-abf5-4f11-9d4e-0733697c591b.png" alt="The image shows an incomplete form being submitted and a correspoding error message" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Try accessing the admin panel at <a href="http://127.0.0.1:8000/admin/">http://127.0.0.1:8000/admin/</a> to see your workouts there as well.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/cdfbcbf3-f7b6-4b97-92ee-dcc27e2ce60c.png" alt="This image shows the added workouts in Django admin" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>If everything works as expected, you're ready to put your app on the internet.</p>
<h2 id="heading-step-10-how-to-prepare-for-deployment">Step 10: How to Prepare for Deployment</h2>
<p>Running your app on localhost is great for development, but nobody else can see it. Deployment means putting your app on a server that's accessible from anywhere on the internet.</p>
<p>Before you deploy, you'll need to make a few changes to your project's settings.</p>
<h3 id="heading-101-how-to-update-settings-for-production">10.1 How to Update Settings for Production</h3>
<p>Open <code>fitness_project/settings.py</code> and make the following changes.</p>
<p>First, set <code>DEBUG</code> to <code>False</code>.</p>
<p>During development, <code>DEBUG = True</code> shows detailed error pages that help you fix problems. In production, these error pages would expose sensitive information about your code and server to anyone who triggers an error.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/6b8b491e-d0ae-4a93-80bf-92134e46ff22.png" alt="The image shows the DEBUG being set to False in the settings.py file" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Next, update <code>ALLOWED_HOSTS</code> to include <strong>PythonAnywhere's</strong> <strong>domain</strong>.</p>
<p>This setting tells Django which domain names are allowed to serve your app. Replace yourusername with the actual PythonAnywhere username you will create in the next step.</p>
<pre><code class="language-python">ALLOWED_HOSTS = ['yourusername.pythonanywhere.com']
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/050746f6-d076-41de-8810-08bf539bfda5.png" alt="The image shows the allowed host list being updated to add the pythonanywhere domain" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Finally, add a <code>STATIC_ROOT</code> setting so Django knows where to collect your static files (CSS, JavaScript, images) for production:</p>
<pre><code class="language-python">import os
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/712e0514-42f6-4292-ae52-99f49b6f162b.png" alt="The image shows the code to collect static files" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>These are the minimum changes needed for a basic deployment.</p>
<p>💡 For a production app handling real user data, you would also want to set a secure SECRET_KEY, configure a proper database like PostgreSQL, and set up HTTPS. But for a learning project, these changes are enough.</p>
<h2 id="heading-step-11-how-to-deploy-your-django-app-on-pythonanywhere">Step 11: How to Deploy Your Django App on PythonAnywhere</h2>
<p>PythonAnywhere is a hosting platform designed specifically for Python web applications. It offers a free tier that's perfect for beginner projects, and it handles much of the server configuration that would otherwise be complex to set up on your own.</p>
<h3 id="heading-111-how-to-create-a-pythonanywhere-account">11.1 How to Create a PythonAnywhere Account</h3>
<p>Go to <a href="http://pythonanywhere.com">pythonanywhere.com</a> and sign up for a free "Beginner" account. Remember the username you choose, because your app will be available at <a href="http://yourusername.pythonanywhere.com"><strong>yourusername.pythonanywhere.com</strong></a><strong>.</strong></p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/e64e7ca4-7d7d-40a7-a56b-259bb706461f.png" alt="The image shows the homepage of pythonanywhere" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Now signup to the website. Fill in the username, email and password and click on the free tier or now.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/fde55ec6-16f4-4a4f-8927-7b01d3e36a96.png" alt="The image shows the various tiers of python anywhere websites" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-112-how-to-upload-your-project-files">11.2 How to Upload Your Project Files</h3>
<p>After logging in, you have two options for getting your project files onto PythonAnywhere.</p>
<h4 id="heading-option-a-upload-using-git">Option A: Upload using Git</h4>
<p>If your project is in a Git repository, open a Bash console from the PythonAnywhere dashboard by clicking "Consoles" and then "Bash." Then clone your repository:</p>
<p>git clone <a href="https://github.com/yourusername/fitness-tracker.git">https://github.com/yourusername/fitness-tracker.git</a></p>
<p>In this tutorial, we won't be using Git. Instead we'll follow the second option.</p>
<h4 id="heading-option-b-upload-files-manually">Option B: Upload files manually</h4>
<p>First go your project folder in your computer and created a compressed version of the project.</p>
<p>IMPORTANT NOTE: When you create the compressed file, make sure to first create a copy of the project somewhere and remove the venv and pycache folder before you compress it.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/169dcd52-50f0-44c0-82d4-1dea1196ac88.png" alt="The image shows the project folder being compressed" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Navigate to your home directory and click on upload file tab and upload the compressed file.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/633c7b1f-0088-4d2d-8911-b45e86aa39c2.png" alt="The image shows the compressed file being uploaded to pythonanywhere" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Now we need to unzip the compressed file. To do this, go to the Consoles tab and click on Bash console.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/7774a88e-362e-4680-a19d-32e0ef09fe04.png" alt="The image shows the Consoles tab and bash option" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>The bash console should open. Then type the following command in the console to unzip the folder:</p>
<pre><code class="language-shell">unzip fitness-tracker.zip
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/1ae044ad-8996-4191-868a-33566c2483d9.png" alt="The image shows the result of the unzip command" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-113-how-to-set-up-a-virtual-environment-in-pythonanywhere">11.3 How to Set Up a Virtual Environment in PythonAnywhere</h3>
<p>Open a Bash console from the PythonAnywhere dashboard. Navigate to your project directory and create a fresh virtual environment:</p>
<pre><code class="language-shell">cd fitness-tracker
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/c8113d5d-68a6-400f-ab9f-7c8903485eda.png" alt="The image shows changing the directory to fitness tracker" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Type the following command to install a virtual environment as we've done before and then activate the virtual environment:</p>
<pre><code class="language-shell">python3 -m venv venv

source venv/bin/activate
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/724767df-181c-4fcf-91e0-77c6dac566b0.png" alt="The image shows the virtual environment being created and activated" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Now install Django as before using <code>pip install django</code> command:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/d6c37d87-7497-4ca1-a90f-6cd66422c2e5.png" alt="The image shows django being installed" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-114-how-to-run-migrations-and-create-a-superuser-on-pythonanywhere">11.4 How to Run Migrations and Create a SuperUser on PythonAnywhere</h3>
<p>While you're still in the Bash console with your virtual environment activated, run the migrations to create the database tables on the server:</p>
<pre><code class="language-shell">python manage.py makemigrations

python manage.py migrate

python manage.py createsuperuser
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/0b763118-5a1c-4c3f-87f4-693cc7de0da2.png" alt="The image shows the make migrations and migrate commands running" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/77183d3c-b4bc-40b7-b00f-5f03d2aea2ab.png" alt="The image shows the super user being created" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-114-how-to-configure-the-web-app-in-pythonanywhere">11.4 How to Configure the Web App in Pythonanywhere</h3>
<p>Go to the "Web" tab on the PythonAnywhere dashboard and click "Add a new web app." Follow the setup wizard:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/e1296e93-3e52-4327-b12a-4d4555e80845.png" alt="The image shows the web tab and add a new web app button" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Click "Next" on the domain name step (<em>remember the free tier uses</em> <a href="http://yourusername.pythonanywhere.com"><em>yourusername.pythonanywhere.com</em></a>).</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/24495b99-e1dc-437d-8ce8-47c0c962349f.png" alt="The image shows the web console where you specify the domain name" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Select "Manual configuration" (not "Django" – the manual option gives you more control).</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/db470bd1-bbd1-4c61-b846-b7147d8f820f.png" alt="The image highlight the manual configuration option which should be selected" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Then choose the Python version that matches what you installed. In my case it's 3.13, so I'll choose 3.13</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/dc51d44e-531c-4871-a225-e68b2db5dc65.png" alt="The image shows the Python version what is being selected" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Click on Next button and a WSGI (Web Server Gateway Interface) will be created.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/3565512b-9c84-4b0d-82d9-3964cb0ff46b.png" alt="The image shows the final page before the web app is created" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>With this we've created the web app:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/eda9fe63-01f4-48bc-98f1-07696bb798bf.png" alt="The image shows the final creation of the web app" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>After you've set up the web app, you have to do two more things:</p>
<ul>
<li><p>Set the virtual environment path</p>
</li>
<li><p>Configure the WSGI file</p>
</li>
</ul>
<h3 id="heading-115-how-to-set-the-virtual-environment-path">11.5 How to Set the Virtual Environment Path</h3>
<p>On the <strong>Web</strong> tab, scroll down to the "<strong>Virtualenv</strong>" section and enter the path to your virtual enviroment. The path to the file should be like this:</p>
<pre><code class="language-shell">/home/yourusername/fitness-tracker/venv
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/413ed047-ecc1-440f-a931-4a57aa91348b.png" alt="The image shows the added path of virtual environment" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-116-how-to-configure-the-wsgi-file">11.6 How to Configure the WSGI file</h3>
<p>Still on the Web tab, scroll to the code section and click on the WSGI configuration file link:</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/934d1542-79d6-4140-8bb2-a4389fc17770.png" alt="The image shows the Code section and the WSGI configuration file path" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Delete all the contents and replace them with the content below and save the file:</p>
<pre><code class="language-python">import os
import sys
path = '/home/prabodhtuladhardev/fitness-tracker' #replace with your username
if path not in sys.path:
    sys.path.append(path)

os.environ['DJANGO_SETTINGS_MODULE'] = 'fitness_project.settings'

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/416adac6-61d7-464d-b91f-b3b35272ef08.png" alt="The image shows the edited wsgi.py file and the highlights the save button" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-117-how-to-set-up-static-files">11.7 How to Set Up Static Files</h3>
<p>Still on the "Web" tab, scroll down to the "Static files" section. Add an entry:</p>
<ul>
<li><p>URL: <code>/static/</code></p>
</li>
<li><p>Directory: <code>/home/yourusername/fitness-tracker/staticfiles</code></p>
</li>
</ul>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/f128dbd2-4acc-4387-89f8-d79de8c1e66e.png" alt="The image shows the static files section of the Web tab" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Then go back to your Bash console and run the following command:</p>
<pre><code class="language-shell">python manage.py collectstatic
</code></pre>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/207f8e67-2679-4ccc-8ebf-7d8aae5d7494.png" alt="The image shows the results of the collect static command" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>This copies all static files to the staticfiles directory so PythonAnywhere can serve them directly.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/a14b75c4-cc1e-48ad-baf2-cf4f00906b85.png" alt="The image shows the folder named static files that was created" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Go back to the "Web" tab and click the green "Reload" button at the top. This restarts your app with all the new configuration.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/08fab36e-8c8d-4d1b-8342-6e340fcf45d4.png" alt="The image shows the web tab with the reload button" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h3 id="heading-118-how-to-view-your-live-application">11.8 How to View Your Live Application</h3>
<p>Open a new browser tab and visit <a href="https://yourusername.pythonanywhere.com">https://yourusername.pythonanywhere.com</a>. You should see your fitness tracker, live on the internet.</p>
<p>Try adding a workout.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/db67dbb1-3cb9-4b86-bfaa-67427ef5eac0.png" alt="The image shows the workout list view being opened in python anywhere" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Visit the admin panel at <a href="https://yourusername.pythonanywhere.com/admin/">https://yourusername.pythonanywhere.com/admin/</a>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/bd80d1e8-cf23-4ac2-b6a7-35bfdf61d023.png" alt="The image shows the workout django admin being opened in pythonanywhere" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<p>Everything should work just as it did on your local machine, but now anyone with the link can access it.</p>
<p>This is a meaningful milestone. You've gone from zero to a deployed Django application. Share the link with a friend or post it in a coding community. Seeing your work live on the internet is one of the most motivating experiences in learning to code.</p>
<h2 id="heading-common-mistakes-and-how-to-fix-them">Common Mistakes and How to Fix Them</h2>
<p>Even when you follow each step carefully, things can go wrong. Here are the most common issues beginners run into and how to solve them.</p>
<p><strong>"ModuleNotFoundError: No module named 'django'"</strong> – This usually means your virtual environment isn't activated. Run <code>source venv/bin/activate</code> (macOS/Linux) or <code>venv\Scripts\activate</code> (Windows) and try again. On PythonAnywhere, make sure the <strong>virtualenv</strong> path in the "<strong>Web</strong>" tab points to the correct location.</p>
<p><strong>"DisallowedHost" error</strong> – You forgot to add your domain to <code>ALLOWED_HOSTS</code> in <code>settings.py</code>, or there's a typo. Double-check that it matches your PythonAnywhere URL exactly.</p>
<p><strong>Static files not loading in production</strong> – Make sure you ran <code>python manage.py collectstatic</code> and that the static file mapping on PythonAnywhere points to the <strong>correct staticfiles</strong> directory. Also verify that <code>STATIC_ROOT</code> is set in <code>settings.py</code>.</p>
<p><strong>"No such table" or migration errors</strong> – You probably forgot to run <code>python manage.py migrate</code> after cloning or uploading your project to PythonAnywhere. Run the <code>migrate</code> command in the Bash console.</p>
<p><strong>Changes not showing up on PythonAnywhere</strong> – After making any code changes, you must click the "<strong>Reload</strong>" button on the "<strong>Web</strong>" tab. PythonAnywhere does not automatically detect file changes.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69bdd408475ca17974459537/08fab36e-8c8d-4d1b-8342-6e340fcf45d4.png" alt="The image shows the web tab and the reload buttton" style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h2 id="heading-how-you-can-improve-this-project">How You Can Improve This Project</h2>
<p>The fitness tracker you built is intentionally simple. That's a feature, not a limitation. A working simple project is the perfect foundation for learning more.</p>
<p>Here are some ideas for expanding it.</p>
<ol>
<li><p><strong>Add user authentication:</strong> Right now, anyone who visits the site sees the same workout data. Django has a built-in authentication system that lets you add registration, login, and logout. Each user could then have their own private list of workouts.</p>
</li>
<li><p><strong>Add the ability to edit and delete workouts.</strong> Currently, once a workout is saved, there's no way to change or remove it from the interface (you can do it through the admin panel, but not the main app). Try creating new views and templates for editing and deleting.</p>
</li>
<li><p><strong>Add workout categories or tags.</strong> Let users categorize their workouts as "Cardio," "Strength," "Flexibility," and so on. This would involve adding a new field to the model or creating a separate Category model with a foreign key relationship.</p>
</li>
<li><p><strong>Add charts and progress tracking.</strong> Use a JavaScript charting library like Chart.js to display workout trends over time. For example, you could show a bar chart of total minutes exercised per week.</p>
</li>
<li><p><strong>Build an API with Django REST Framework.</strong> If you want to learn about building APIs, try installing Django REST Framework (DRF) and creating API endpoints for your workouts. This would let you build a mobile app or a separate front end that communicates with your Django back end.</p>
</li>
</ol>
<p>Each of these improvements will teach you something new about Django while building on the foundation you already have.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You've built a fully functional fitness tracker web app with Django and deployed it to the internet. That's no small achievement.</p>
<p>Along the way, you learned how Django projects and apps are structured, how models define the shape of your data, how migrations translate those models into database tables, how views handle the logic of your application, how templates render dynamic HTML, and how URLs tie everything together. You also went through the entire deployment process on PythonAnywhere.</p>
<p>These are the core building blocks of Django development. The patterns you practiced here – defining a model, creating a form, writing a view, building a template, and connecting a URL – are the same patterns you will use in every Django project, no matter how complex.</p>
<p>The best way to solidify what you have learned is to keep building. Try one of the improvements mentioned above, or start a completely new project. A calorie tracker, a habit tracker, an expense tracker, or a personal journal would all use the same Django concepts with slightly different models and views.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Go From Hello World to Building Real-World Applications ]]>
                </title>
                <description>
                    <![CDATA[ Many developers start learning programming by building simple projects like todo apps, calculators, and basic CRUD applications. These projects are useful at the beginning, as they help you understand how a programming language works and give you the... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-go-from-hello-world-to-building-real-world-applications/</link>
                <guid isPermaLink="false">697d098607632dbd100433e8</guid>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Learning Journey ]]>
                    </category>
                
                    <category>
                        <![CDATA[ coding ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Spruce Emmanuel ]]>
                </dc:creator>
                <pubDate>Fri, 30 Jan 2026 19:41:58 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769802056548/2b1b2ede-5f7f-423f-b6ee-19e0c0ac7b43.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Many developers start learning programming by building simple projects like todo apps, calculators, and basic CRUD applications. These projects are useful at the beginning, as they help you understand how a programming language works and give you the confidence to start building things. But for many developers, progress stops there.</p>
<p>Real world applications aren’t just about showing data on a screen. They solve real problems, work with real users, and handle situations that don’t always go as planned. This is where many developers struggle.</p>
<p>When I review junior developers’ résumés, I notice a common pattern: the projects section is often filled with beginner apps that look very similar. In today’s job market, this is usually not enough. Employers want to see that you can build something useful, something people would actually use.</p>
<p>The goal of this article is to help you move past simple Hello World projects like todo apps, calculators, and basic CRUD applications. By the end, you’ll understand how to approach building real applications that solve real problems and feel closer to what is built in the real world.</p>
<p>You might be wondering why you should listen to me.</p>
<p>Over the last 10 years, I’ve spent a lot of time building, breaking, and rebuilding software. I have experimented, failed many times, and eventually built applications that thousands of people use every day. A few weeks ago, I launched one of my own SaaS products and watched real users use it at scale, with over a thousand active users during peak hours, without the system crashing.</p>
<p>I’m sharing this because I’ve been where you are now.</p>
<p>If you continue reading, you will:</p>
<ul>
<li><p>Learn from real experience building applications used by real users</p>
</li>
<li><p>Learn what not to do, based on years of mistakes and lessons</p>
</li>
<li><p>Learn what actually works and what helps you stand out as a junior developer</p>
</li>
<li><p>Clear up common misconceptions that slow people down</p>
</li>
</ul>
<p>This article is not about theory. It’s about building real things.</p>
<p>To drive this point home, we’ll build a real world application that solves a real problem and is used by real people. Along the way, you’ll learn how to come up with real application ideas, how to build them with simple tools, and how to serve them to many users.</p>
<p>If this sounds like something you’re up for, then let’s get started.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-you-probably-think-you-dont-know-enough">You Probably Think You Don’t Know Enough</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-find-real-world-application-ideas">How to Find Real World Application Ideas</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-are-we-going-to-build">What Are We Going to Build</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-1-setting-up-the-backend">Step 1: Setting Up the Backend</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-adding-background-removal-to-the-backend">Step 2: Adding Background Removal to the Backend</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-building-the-frontend">Step 3: Building the Frontend</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-running-and-testing-the-application-locally">Running and Testing the Application Locally</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-putting-the-backend-on-the-internet">Step 4: Putting the Backend on the Internet</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-deploying-the-backend-on-cloud-run">Deploying the Backend on Cloud Run</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-updating-the-frontend-to-use-the-live-backend">Updating the Frontend to Use the Live Backend</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-step-5-making-the-backend-and-frontend-work-together-cors">Step 5: Making the Backend and Frontend Work Together CORS</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-6-putting-the-frontend-on-the-internet">Step 6: Putting the Frontend on the Internet</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts-what-you-just-built-matters">Final Thoughts What You Just Built Matters</a></p>
</li>
</ol>
<h2 id="heading-you-probably-think-you-dont-know-enough"><strong>You Probably Think You Don't Know Enough</strong></h2>
<p>Before we begin, I want to clear up the most common misconception beginners have.</p>
<p>Many developers believe they don’t know enough to start building real world applications. They think they need to learn more JavaScript, more Python, or another framework before they are ready. Some even think learning React is the final step that will suddenly make everything click.</p>
<p>This belief is very common, but it’s not true.</p>
<p>If you know how to write HTML, CSS, and a simple loop or function in any programming language, you already have most of what it takes to build a real world application. This tutorial is proof of that.</p>
<p>That’s why I am writing this article. Not to tell you to learn more first, but to show you how far you can go with what you already know.</p>
<h2 id="heading-how-to-find-real-world-application-ideas">How to Find Real World Application Ideas</h2>
<p>One question beginners ask a lot is, “What kind of apps should I build?”</p>
<p>This is rarely talked about, but it matters more than most people think.</p>
<p>A simple rule is this: build things that already exist.</p>
<p>Look at the apps you use every day, especially the simple ones. Tools that do one thing well. If you find yourself using an app often, that app is solving a real problem.</p>
<p>For example, people use background removers to clean up images. They use URL shorteners to share links. They use notes apps to save quick thoughts. None of these ideas are new, but they are real.</p>
<p>You don’t need to invent something original. You need to understand a problem and build a working solution for it.</p>
<p>When you replicate real tools, you naturally learn how real applications are structured. You also end up with projects that make sense on a résumé, because they solve problems people recognize.</p>
<p>That’s exactly what we are going to do in this tutorial.</p>
<h2 id="heading-what-are-we-going-to-build">What Are We Going to Build?</h2>
<p>I’ll tell you now, it is not going to be another Hello World application. We’re going to be building a background remover web application.</p>
<p>This is the kind of tool people actually use. Designers use it for images, content creators use it for thumbnails, and developers build similar features into real products. It works with real files, real data, and real results.</p>
<p>If you want to see the final result before we start building, you can try the working app here: <a target="_blank" href="https://iamspruce.github.io/background-remover/">https://iamspruce.github.io/background-remover/</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769502551999/c2bdf45f-8768-4dc8-a816-6fa9a05dc15b.png" alt="The background remover application we are about to build" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>We’ll build this app using simple tools on purpose. Plain HTML, CSS, and JavaScript for the frontend, and Python for the backend. No heavy frameworks and no complicated setup.</p>
<p>Before we continue, let me clear up <strong>another common misconception.</strong></p>
<p>Many developers believe that for an application to be taken seriously, it must be built with complex frameworks. In my experience building applications for clients around the world over the last 10 years, not a single client has ever asked me what framework I used.</p>
<p>They only cared about one thing: Did it work, and did it solve their problem?</p>
<p>Users won’t care how your app is built. They care that it works. If HTML, CSS, and JavaScript can solve the problem, there’s no reason to wait months just to learn a new framework.</p>
<p>Now that we understand <em>why</em> we’re building this app and <em>what</em> problem it solves, it’s time to start writing code.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we start writing code, let’s quickly talk about what you need.</p>
<p>This tutorial is not for absolute beginners, but it’s also not super advanced. If you’ve built small things before and you want to build something that actually feels real, you’re in the right place.</p>
<h3 id="heading-what-you-should-already-know">What You Should Already Know</h3>
<p>You should be comfortable with:</p>
<ul>
<li><p>Basic HTML: You know what inputs, buttons, images, and divs do.</p>
</li>
<li><p>Basic CSS: You can style a page and make it look presentable.</p>
</li>
<li><p>Basic JavaScript: You know how to listen for a button click and send a request using <code>fetch</code>.</p>
</li>
</ul>
<p>That’s enough to follow along.</p>
<p>You don’t need React or any other frontend framework.</p>
<h3 id="heading-backend-knowledge">Backend Knowledge</h3>
<p>For the backend, you don’t need to be a Python expert.</p>
<p>You just need to understand that:</p>
<ul>
<li><p>Python can run a server</p>
</li>
<li><p>A server can receive requests</p>
</li>
<li><p>A server can send back responses</p>
</li>
</ul>
<p>Everything else will be explained as we go.</p>
<h3 id="heading-tools-you-need">Tools You Need</h3>
<p>Make sure you have these installed:</p>
<ul>
<li><p>Python 3.9 or newer</p>
</li>
<li><p>Git</p>
</li>
<li><p>A code editor like VS Code</p>
</li>
<li><p>A browser</p>
</li>
</ul>
<p>No Docker knowledge or cloud experience required.</p>
<h3 id="heading-github-account-important">GitHub Account (Important)</h3>
<p>We’ll deploy the backend directly from GitHub, so you’ll need a GitHub account.</p>
<p>If you’ve never pushed a project to GitHub or hosted one before, I’ve already written a <a target="_blank" href="https://www.freecodecamp.org/news/host-your-first-project-on-github/">beginner-friendly guide</a> you can follow first.</p>
<p>Read that article, then come back here.</p>
<h3 id="heading-a-quick-mindset-check">A Quick Mindset Check</h3>
<p>This is not a copy-paste tutorial. You will see real errors. Things may break. That’s normal. That’s how real applications are built.</p>
<p>Now that we’re clear on what you need, let’s start writing code.</p>
<p>We’ll begin with the backend. This is an important decision, so let’s explain it properly.</p>
<h2 id="heading-step-1-setting-up-the-backend">Step 1: Setting Up the Backend</h2>
<h3 id="heading-what-is-a-backend-and-why-do-we-need-one">What Is a Backend and Why Do We Need One?</h3>
<p>A backend is a program that runs on a server and does the heavy work for an application.</p>
<p>In our case, the heavy work is image processing. Removing a background from an image requires libraries that can’t run inside the browser. Browsers are designed for safety and user interaction, not for this kind of processing.</p>
<p>That’s why we need a backend.</p>
<p>The backend will:</p>
<ul>
<li><p>Receive an image from the user</p>
</li>
<li><p>Remove the background</p>
</li>
<li><p>Send the processed image back</p>
</li>
</ul>
<p>The frontend will simply talk to this backend later.</p>
<h3 id="heading-keeping-things-simple-on-purpose">Keeping Things Simple on Purpose</h3>
<p>Because this might be your first real project, we’re going to keep the backend as simple as possible.</p>
<ul>
<li><p>One programming language (Python)</p>
</li>
<li><p>One backend file</p>
</li>
<li><p>No complex folder structure</p>
</li>
<li><p>No advanced concepts</p>
</li>
</ul>
<p>This is intentional. Real world applications don’t have to start out complex. They typically start small and grow.</p>
<h3 id="heading-project-structure">Project Structure</h3>
<p>Create a new folder called:</p>
<pre><code class="lang-bash">background-remover
</code></pre>
<p>Inside it, create a folder for the backend:</p>
<pre><code class="lang-bash">background-remover/
  backend/
</code></pre>
<p>Move into the backend folder:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> background-remover/backend
</code></pre>
<p>Now create a virtual environment:</p>
<pre><code class="lang-bash">python -m venv env
</code></pre>
<p>A virtual environment keeps this project’s dependencies separate from everything else on your computer. This is standard practice and something you’ll see in real projects.</p>
<p>Now activate it:</p>
<p>macOS or Linux:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> env/bin/activate
</code></pre>
<p>Windows:</p>
<pre><code class="lang-bash">env\Scripts\activate
</code></pre>
<p>Now create a folder for your application code:</p>
<pre><code class="lang-bash">mkdir api
</code></pre>
<p>Your structure should now look like this:</p>
<pre><code class="lang-bash">background-remover/
  backend/
    env/
    api/
</code></pre>
<p>At this stage, nothing looks impressive yet. That’s normal.</p>
<h3 id="heading-installing-fastapi">Installing FastAPI</h3>
<p>We’ll use FastAPI to build the backend API.</p>
<p>Install it together with Uvicorn, which is the server that runs our app:</p>
<pre><code class="lang-bash">pip install fastapi uvicorn
</code></pre>
<p>FastAPI allows us to define endpoints clearly and with very little code, which is perfect for us.</p>
<h3 id="heading-creating-the-first-backend-file">Creating the First Backend File</h3>
<p>Inside the <code>api</code> folder, create a file called <code>main.py</code>.</p>
<p>Add the following code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI

app = FastAPI()

<span class="hljs-meta">@app.get("/health")</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">health</span>():</span>
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"status"</span>: <span class="hljs-string">"ok"</span>}
</code></pre>
<p>Let’s pause and understand what this does.</p>
<ul>
<li><p>We created a FastAPI application</p>
</li>
<li><p>We added a <code>/health</code> endpoint</p>
</li>
<li><p>This endpoint simply returns a message</p>
</li>
</ul>
<p>Before building real features, developers always confirm that their server actually runs. That is exactly what this endpoint is for.</p>
<h3 id="heading-running-the-server">Running the Server</h3>
<p>From inside the <code>backend</code> folder, start the server:</p>
<pre><code class="lang-bash">uvicorn api.main:app --reload
</code></pre>
<p>Now open a new terminal and run:</p>
<pre><code class="lang-bash">curl http://localhost:8000/health
</code></pre>
<p>You should see:</p>
<pre><code class="lang-bash">{<span class="hljs-string">"status"</span>:<span class="hljs-string">"ok"</span>}
</code></pre>
<p>This is an important moment.</p>
<p>You now have:</p>
<ul>
<li><p>A running backend server</p>
</li>
<li><p>A real HTTP endpoint</p>
</li>
<li><p>A response coming from your own code</p>
</li>
</ul>
<p>This is how real backend services start.</p>
<h3 id="heading-why-we-did-this-first">Why We Did This First</h3>
<p>At this point, you might wonder why we didn’t jump straight into background removal.</p>
<p>The reason is simple: if the server doesn’t run, nothing else matters.</p>
<p>By starting with a health endpoint, we removed uncertainty. We know the server works. Everything we add next is built on top of something we already know is working.</p>
<p>Now that the foundation is in place, we can move on to the real feature.</p>
<h2 id="heading-step-2-adding-background-removal-to-the-backend">Step 2: Adding Background Removal to the Backend</h2>
<p>Before we write any code here, we need to clear up an important misconception.</p>
<h3 id="heading-a-common-misconception-about-machine-learning">A Common Misconception About Machine Learning</h3>
<p>When people hear “background removal,” they often think machine learning is too advanced for them.</p>
<p>In real world development, this is almost never how it works.</p>
<p>You aren’t expected to build machine learning models yourself. You use libraries created by others and focus on integrating them correctly.</p>
<p>That is exactly what we’re doing here.</p>
<h3 id="heading-installing-the-background-removal-library">Installing the Background Removal Library</h3>
<p>Install the required packages:</p>
<pre><code class="lang-bash">pip install rembg pillow onnxruntime
</code></pre>
<ul>
<li><p><code>rembg</code> handles the background removal</p>
</li>
<li><p><code>pillow</code> helps us work with images</p>
</li>
</ul>
<p>For our purposes here, you don’t need to understand how these libraries work internally. You only need to know how to use them.</p>
<h3 id="heading-adding-the-background-removal-endpoint">Adding the Background Removal Endpoint</h3>
<p>Now update <code>main.py</code> so it looks like this:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, UploadFile, File
<span class="hljs-keyword">from</span> rembg <span class="hljs-keyword">import</span> remove
<span class="hljs-keyword">from</span> PIL <span class="hljs-keyword">import</span> Image
<span class="hljs-keyword">import</span> io

app = FastAPI()

<span class="hljs-meta">@app.get("/health")</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">health</span>():</span>
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"status"</span>: <span class="hljs-string">"ok"</span>}

<span class="hljs-meta">@app.post("/remove-bg")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">remove_bg</span>(<span class="hljs-params">file: UploadFile = File(<span class="hljs-params">...</span>)</span>):</span>
    image_bytes = <span class="hljs-keyword">await</span> file.read()
    image = Image.open(io.BytesIO(image_bytes))

    output = remove(image)

    buffer = io.BytesIO()
    output.save(buffer, format=<span class="hljs-string">"PNG"</span>)
    buffer.seek(<span class="hljs-number">0</span>)

    <span class="hljs-keyword">return</span> buffer.getvalue()
</code></pre>
<p>Let’s explain this carefully.</p>
<ul>
<li><p>The endpoint accepts an uploaded image</p>
</li>
<li><p>The image is read into memory</p>
</li>
<li><p>The background is removed</p>
</li>
<li><p>The result is saved as a PNG with transparency</p>
</li>
<li><p>The image is returned to the client</p>
</li>
</ul>
<p>This endpoint is the core of our application.</p>
<h3 id="heading-testing-the-endpoint-with-curl">Testing the Endpoint With curl</h3>
<p>Before building the frontend, we’ll test the backend directly.</p>
<p>Run this command:</p>
<pre><code class="lang-javascript">curl -X POST \
  -F <span class="hljs-string">"file=@person.jpg"</span> \
  <span class="hljs-attr">http</span>:<span class="hljs-comment">//localhost:8000/remove-bg \</span>
  --output result.png
</code></pre>
<p>Open <code>result.png</code>. If you see the background removed, then the backend is complete.</p>
<p>At this point, you have built a backend that:</p>
<ul>
<li><p>Accepts real user input</p>
</li>
<li><p>Processes real data</p>
</li>
<li><p>Returns a meaningful result</p>
</li>
</ul>
<p>This is a real backend.</p>
<h3 id="heading-where-we-are-now">Where We Are Now</h3>
<p>Let’s pause and summarize:</p>
<ul>
<li><p>We set up a backend server</p>
</li>
<li><p>We confirmed that it runs</p>
</li>
<li><p>We added a real feature</p>
</li>
<li><p>We tested it without a frontend</p>
</li>
</ul>
<p>This is exactly how real developers work.</p>
<p>In the next section, we’ll build a simple frontend that talks to this backend and turns it into something users can interact with.</p>
<h2 id="heading-step-3-building-the-frontend-what-the-user-actually-sees">Step 3: Building the Frontend (What the User Actually Sees)</h2>
<p>At this point, our backend is working.</p>
<p>It can receive an image, remove the background, and send the result back. But right now, only developers can use it, because it requires terminal commands.</p>
<p>To make this useful to actual (non-technical) people, we need a frontend.</p>
<p>The frontend is simply the part of the application users see and interact with in their browser.</p>
<h3 id="heading-clearing-a-common-misconception-about-frontends">Clearing a Common Misconception About Frontends</h3>
<p>Many beginners think building a frontend means learning a framework first.</p>
<p>This is not true.</p>
<p>Frameworks help later, but they aren’t required to build real applications. Under the hood, every frontend still comes down to HTML, CSS, and JavaScript.</p>
<p>That’s why we are using plain HTML, CSS, and JavaScript here. No React, no build tools, no setup. Just the basics.</p>
<h3 id="heading-what-our-frontend-will-do">What Our Frontend Will Do</h3>
<p>Our frontend has one job. It will:</p>
<ul>
<li><p>Let the user select an image</p>
</li>
<li><p>Send that image to the backend</p>
</li>
<li><p>Receive the processed image</p>
</li>
<li><p>Show it on the screen</p>
</li>
<li><p>Allow the user to download it</p>
</li>
</ul>
<p>That’s all.</p>
<p>If it does these things correctly, it’s a real frontend.</p>
<h3 id="heading-creating-the-frontend">Creating the Frontend</h3>
<p>Go back to the root of your project and create three files:</p>
<pre><code class="lang-bash">index.html
styles.css
app.js
</code></pre>
<p>This simple setup is very common. Each file has a clear responsibility, which makes the code easier to understand.</p>
<h3 id="heading-writing-the-html-page">Writing the HTML Page</h3>
<p>Open <code>index.html</code> and add the following:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Background Remover<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"styles.css"</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Background Remover<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"imageInput"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"removeBtn"</span>&gt;</span>Remove Background<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"result"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"resultImage"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"downloadLink"</span> <span class="hljs-attr">download</span>&gt;</span>Download Image<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"app.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>Right now, this page won’t do anything interesting. That’s expected.</p>
<p>HTML only describes what should be on the page. The behavior comes from JavaScript, which we’ll add after we add some styling.</p>
<h3 id="heading-adding-some-basic-styling">Adding Some Basic Styling</h3>
<p>Open <code>styles.css</code> and add this:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">font-family</span>: sans-serif;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">600px</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">40px</span> auto;
}

<span class="hljs-selector-tag">button</span> {
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">10px</span>;
}

<span class="hljs-selector-class">.result</span> {
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">20px</span>;
}

<span class="hljs-selector-tag">img</span> {
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">100%</span>;
}

<span class="hljs-selector-id">#downloadLink</span> {
  <span class="hljs-attribute">display</span>: none;
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">10px</span>;
}
</code></pre>
<p>This is not about making things look fancy.</p>
<p>The goal here is simply to make the page readable and pleasant to use. Many real internal tools look no better than this. (And you can always improve the styling later if you want.)</p>
<h3 id="heading-connecting-the-frontend-to-the-backend">Connecting the Frontend to the Backend</h3>
<p>Now we’ll write the JavaScript that makes everything work.</p>
<p>Open <code>app.js</code> and add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> imageInput = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"imageInput"</span>);
<span class="hljs-keyword">const</span> removeBtn = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"removeBtn"</span>);
<span class="hljs-keyword">const</span> resultImage = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"resultImage"</span>);
<span class="hljs-keyword">const</span> downloadLink = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"downloadLink"</span>);

removeBtn.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> file = imageInput.files[<span class="hljs-number">0</span>];

  <span class="hljs-keyword">if</span> (!file) {
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-keyword">const</span> formData = <span class="hljs-keyword">new</span> FormData();
  formData.append(<span class="hljs-string">"file"</span>, file);

  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"http://localhost:8000/remove-bg"</span>, {
    <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
    <span class="hljs-attr">body</span>: formData,
  });

  <span class="hljs-keyword">const</span> blob = <span class="hljs-keyword">await</span> response.blob();
  <span class="hljs-keyword">const</span> imageUrl = URL.createObjectURL(blob);

  resultImage.src = imageUrl;
  downloadLink.href = imageUrl;
  downloadLink.style.display = <span class="hljs-string">"inline"</span>;
});
</code></pre>
<p>Let’s slow down and explain what’s happening here:</p>
<ul>
<li><p>We read the image selected by the user</p>
</li>
<li><p>We wrap it in <code>FormData</code> so it can be sent to the backend</p>
</li>
<li><p>We send it to our <code>/remove-bg</code> endpoint</p>
</li>
<li><p>We receive the processed image back</p>
</li>
<li><p>We display it and prepare it for download</p>
</li>
</ul>
<p>This is the moment where the frontend and backend finally talk to each other.</p>
<h3 id="heading-running-the-frontend-with-live-server">Running the Frontend With Live Server</h3>
<p>Before testing, make sure your backend is still running.</p>
<p>Now, instead of opening <code>index.html</code> directly, use the <strong>Live Server</strong> extension in VS Code.</p>
<p>If you don’t have it installed, just open VS Code extensions, search for “Live Server”, and then install it. Then right click on <code>index.html</code> and click “Open with Live Server”.</p>
<p>This starts a small local server for the frontend.</p>
<p>Why does this matter?</p>
<p>Many browser features work better when files are served through a server instead of opened directly from the file system. Using Live Server also matches how real frontends are served.</p>
<h3 id="heading-testing-the-full-application-locally">Testing the Full Application Locally</h3>
<p>Now choose an image and click the button.</p>
<p>If everything is working:</p>
<ul>
<li><p>The image is sent to the backend</p>
</li>
<li><p>The background is removed</p>
</li>
<li><p>The result appears on the page</p>
</li>
<li><p>The download link shows up</p>
</li>
</ul>
<p>Take a moment here. You have just built:</p>
<ul>
<li><p>A backend that processes real data</p>
</li>
<li><p>A frontend that talks to it</p>
</li>
<li><p>A complete application running locally</p>
</li>
</ul>
<p>This is already more than a “Hello World” project.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769503437045/6a984e58-cee9-4255-84aa-76ee26b805b3.png" alt="The application running locally after connecting the frontend and backend and removing background from an image" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-clearing-one-more-misconception">Clearing One More Misconception</h3>
<p>Some beginners look at this and think: “This feels too simple to be a real app.”</p>
<p>This is another misconception. Real applications aren’t defined by complexity. They’re defined by usefulness. If your app solves a real problem and people can use it, it’s a real application.</p>
<p>In the next section, we’ll take this exact app and put it on the internet so anyone can use it.</p>
<h2 id="heading-step-4-putting-the-backend-on-the-internet">Step 4: Putting the Backend on the Internet</h2>
<p>Right now, your backend is running on your computer.</p>
<p>That means:</p>
<ul>
<li><p>It works only for you</p>
</li>
<li><p>If you close your laptop, it stops</p>
</li>
<li><p>If someone opens your frontend, it cannot reach your backend</p>
</li>
</ul>
<p>To fix this, we need to run the backend on a computer that is <strong>always online</strong>.</p>
<p>This process is called <strong>deployment</strong>.</p>
<h3 id="heading-a-simple-way-to-think-about-deployment">A Simple Way to Think About Deployment</h3>
<p>Deployment does <strong>not</strong> mean writing new code.</p>
<p>It simply means this: instead of running your backend on your laptop, you run it on another computer that never sleeps.</p>
<p>Everything we do next exists only to make that happen.</p>
<h3 id="heading-why-we-arent-using-netlify-or-vercel">Why We Aren’t Using Netlify or Vercel</h3>
<p>You might be wondering why we aren’t using Netlify or Vercel. Those platforms are great, but they’re mainly for <strong>frontends</strong>.</p>
<p>They work best when your app is:</p>
<ul>
<li><p>Static HTML, CSS, and JavaScript</p>
</li>
<li><p>A frontend framework like React or Vue</p>
</li>
<li><p>Small serverless functions</p>
</li>
</ul>
<p>Our backend is different. It’s a <strong>Python server</strong> that:</p>
<ul>
<li><p>Stays running</p>
</li>
<li><p>Accepts image uploads</p>
</li>
<li><p>Processes images</p>
</li>
<li><p>Uses heavy native libraries for background removal</p>
</li>
</ul>
<p>This kind of backend needs a <strong>real server</strong>, not a lightweight serverless function.</p>
<p>That’s why Netlify and Vercel aren’t a good fit here.</p>
<h3 id="heading-why-were-using-cloud-run">Why We’re Using Cloud Run</h3>
<p>Instead, we’re using <strong>Cloud Run</strong>.</p>
<p>Cloud Run lets us run real backend servers on Google’s infrastructure without managing servers ourselves.</p>
<p>We’re using it because:</p>
<ul>
<li><p>It supports full Python backends</p>
</li>
<li><p>It deploys directly from GitHub</p>
</li>
<li><p>It handles scaling and servers for us</p>
</li>
<li><p>It works well with heavy workloads</p>
</li>
<li><p>It’s beginner-friendly</p>
</li>
</ul>
<p>Most importantly, it lets you deploy a <strong>real backend</strong> without learning cloud commands or CI/CD pipelines.</p>
<h3 id="heading-preparing-the-backend-for-cloud-run">Preparing the Backend for Cloud Run</h3>
<p>Before deploying, we need to make sure our backend is ready.</p>
<h4 id="heading-creating-requirementstxt">Creating <code>requirements.txt</code></h4>
<p>Inside the <code>backend</code> folder, create a file called <code>requirements.txt</code>.</p>
<p>Add this:</p>
<pre><code class="lang-go">fastapi
uvicorn
rembg
pillow
onnxruntime
</code></pre>
<p>This file tells Cloud Run which Python libraries to install.</p>
<h3 id="heading-creating-a-github-repository">Creating a GitHub Repository</h3>
<p>Cloud Run deploys directly from GitHub, so our code must live there.</p>
<p>From the project root, run:</p>
<pre><code class="lang-go">git init
git add .
git commit -m <span class="hljs-string">"Initial background remover project"</span>
git branch -M main
git remote add origin YOUR_REPO_URL
git push -u origin main
</code></pre>
<p>This <strong>single repository</strong> will be used for both backend and frontend.</p>
<h3 id="heading-deploying-the-backend-on-cloud-run">Deploying the Backend on Cloud Run</h3>
<p>Now open your browser and go to <strong>Google Cloud Console</strong>.</p>
<h4 id="heading-1-create-a-new-project">1. Create a New Project</h4>
<p>Open <a target="_blank" href="https://console.cloud.google.com">Google Cloud Console</a> and click New Project. Give it a name (for example: <code>background-remover</code>) and then click Create.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769749541636/778464de-2c95-4f7e-9505-9729a84f2001.png" alt="Creating a new Google Cloud project" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h4 id="heading-2-open-cloud-run">2. Open Cloud Run</h4>
<p>Use the search bar at the top and search for <strong>Cloud Run.</strong> Then open it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769749581066/9078ae2e-fa00-4dfa-be04-383d9702bb90.png" alt="Cloud Run overview page" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Cloud Run will automatically enable the required APIs.</p>
<p>You will be asked to set up billing. Google gives you $300 free credit, which is more than enough for this tutorial.</p>
<h4 id="heading-3-start-creating-the-service">3. Start Creating the Service</h4>
<p>Click Create Service and choose Deploy continuously from a repository.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769749636809/348f720a-b1b1-46e4-9eab-48928adeb2f3.png" alt="Creating a Cloud Run service from a repository" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h4 id="heading-4-connect-your-github-account">4. Connect Your GitHub Account</h4>
<p>Select <strong>GitHub</strong> as the repository provider and authenticate your GitHub account. Then install Google Cloud Build on your GitHub account. Choose <strong>only the repository</strong> you want to deploy.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769749686780/89571b5e-0bd7-4127-82d2-b9445a5044e4.png" alt="Installing Google Cloud Build on GitHub" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>This allows Google Cloud to build and deploy your code automatically.</p>
<h4 id="heading-5-select-the-repository">5. Select the Repository</h4>
<p>Then choose the repository you just installed Cloud Build on and select the <code>main</code> branch.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769749745522/a9f95c4b-7fd3-480d-bae1-7ae92bcdd52f.png" alt="Selecting the GitHub repository" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h4 id="heading-6-configure-the-build">6. Configure the Build</h4>
<p>Now comes the important part: building the context directory.</p>
<p>Set this to:</p>
<pre><code class="lang-bash">backend
</code></pre>
<p>This tells Cloud Run:</p>
<blockquote>
<p>“My backend code lives inside the <code>backend</code> folder.”</p>
</blockquote>
<p>For the entry command, enter:</p>
<pre><code class="lang-bash">uvicorn api.main:app --host 0.0.0.0 --port 8080
</code></pre>
<p>This is how Cloud Run starts your FastAPI server.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769749801126/87676cce-3e75-4244-b32f-401f782d7c85.png" alt="Build configuration for the backend" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h4 id="heading-7-configure-container-resources">7. Configure Container Resources</h4>
<p>Our backend runs a <strong>background-removal model</strong>, which is heavy.</p>
<p>So we must increase resources.</p>
<ul>
<li><p>Change memory from <strong>512 MB → 2 GB</strong></p>
</li>
<li><p>Set CPU to <strong>4</strong></p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769749870471/a24e5d37-af21-48a6-b720-b6748d478b6d.png" alt="Increasing memory and CPU" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>This ensures the model can load and run properly.</p>
<h4 id="heading-8-deploy">8. Deploy</h4>
<p>Now click <strong>Create</strong>.</p>
<p>Cloud Run will:</p>
<ul>
<li><p>Build your app</p>
</li>
<li><p>Install dependencies</p>
</li>
<li><p>Create a container</p>
</li>
<li><p>Deploy it to the internet</p>
</li>
</ul>
<p>You’ll see logs showing the build and deployment process.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769749916316/5eb276f6-01c1-413c-aeb7-13a8d34dbbe2.png" alt="Cloud Build running" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>This can take a few minutes. That’s normal.</p>
<h3 id="heading-checking-that-the-backend-is-live">Checking That the Backend Is Live</h3>
<p>Once deployment finishes, Cloud Run will show you a <strong>public URL</strong>.</p>
<p>Test it:</p>
<pre><code class="lang-bash">curl https://YOUR_CLOUD_RUN_URL/health
</code></pre>
<p>If you see:</p>
<pre><code class="lang-bash">{<span class="hljs-string">"status"</span>:<span class="hljs-string">"ok"</span>}
</code></pre>
<p>Your backend is officially live on the internet.</p>
<p>Pause here for a second.</p>
<p>You just deployed a real backend. Congratulations.</p>
<h3 id="heading-updating-the-frontend-to-use-the-live-backend">Updating the Frontend to Use the Live Backend</h3>
<p>Open <code>app.js</code>.</p>
<p>Replace:</p>
<pre><code class="lang-bash">http://localhost:8000/remove-bg
</code></pre>
<p>With:</p>
<pre><code class="lang-bash">https://YOUR_CLOUD_RUN_URL/remove-bg
</code></pre>
<p>Save the file and reload the frontend.</p>
<h2 id="heading-step-5-making-the-backend-and-frontend-work-together"><strong>Step 5: Making the Backend and Frontend Work Together</strong></h2>
<p>At this point, we have two things:</p>
<ul>
<li><p>A backend running on the internet</p>
</li>
<li><p>A frontend running in the browser</p>
</li>
</ul>
<p>Now we want them to talk to each other.</p>
<p>Open your frontend, select an image, and click the button. You’ll notice that it still doesn’t work. This is expected.</p>
<h3 id="heading-what-is-happening-here">What Is Happening Here?</h3>
<p>Your frontend is running on one address, while your backend is running on another address.</p>
<p>Browsers are very strict about this. By default, a browser will block requests from one website to another unless the backend explicitly allows it. This is a security feature.</p>
<p>This rule is called <strong>CORS</strong>.</p>
<h3 id="heading-clearing-a-common-misconception-about-cors">Clearing a Common Misconception About CORS</h3>
<p>When beginners see a CORS error, they often think something is broken.</p>
<p>Nothing is broken. CORS is simply the browser saying: “I need the backend to confirm that this frontend is allowed to talk to it.”</p>
<p>So all we need to do is tell the backend: “It’s okay for requests to come from my frontend.”</p>
<h3 id="heading-allowing-only-our-frontend-not-everyone">Allowing Only Our Frontend (Not Everyone)</h3>
<p>Instead of allowing requests from everywhere, we’ll allow requests only from our frontend. This is a good habit to learn early.</p>
<p>Open <code>backend/api/main.py</code>.</p>
<p>Add this import at the top:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> fastapi.middleware.cors <span class="hljs-keyword">import</span> CORSMiddleware
</code></pre>
<p>Then, after creating the FastAPI app, add this:</p>
<pre><code class="lang-python">app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        <span class="hljs-string">"http://127.0.0.1:5500"</span>,
        <span class="hljs-string">"http://localhost:5500"</span>
    ],
    allow_methods=[<span class="hljs-string">"POST"</span>],
    allow_headers=[<span class="hljs-string">"*"</span>],
)
</code></pre>
<p>Why these URLs?</p>
<p>If you’re using the Live Server extension, your frontend is usually served on port <code>5500</code>. These are the addresses your browser is using locally.</p>
<p>We’re telling the backend: “Only accept requests from this frontend.”</p>
<p>That is exactly what we want.</p>
<h3 id="heading-redeploying-the-backend">Redeploying the Backend</h3>
<p>Any time you change backend code, you need to redeploy it.</p>
<p>Since our backend is deployed on Cloud Run and set up with automatic deploy, redeploying is simple, all you need to do is push your changes.</p>
<p>From the project root, run:</p>
<pre><code class="lang-go">git add .
git commit -m <span class="hljs-string">"Add CORS configuration"</span>
git push
</code></pre>
<p>Cloud Run will automatically detect the change and redeploy your backend.</p>
<p>Wait for the deployment to finish. Once it’s done, your backend will now allow requests from your local frontend.</p>
<h3 id="heading-testing-again">Testing Again</h3>
<p>Reload your frontend in the browser.</p>
<p>Select an image and click the button. This time, it should work.</p>
<p>You just handled a real browser security rule that every production app runs into. That alone is a huge learning step.</p>
<h2 id="heading-step-6-putting-the-frontend-on-the-internet-github-pages">Step 6: Putting the Frontend on the Internet (GitHub Pages)</h2>
<p>Right now, your frontend works only on your computer.</p>
<p>Just like the backend earlier, this means no one else can use it.</p>
<p>Let’s fix that.</p>
<h3 id="heading-why-github-pages">Why GitHub Pages?</h3>
<p>Our frontend is:</p>
<ul>
<li><p>Just HTML, CSS, and JavaScript</p>
</li>
<li><p>No backend code</p>
</li>
<li><p>No build step</p>
</li>
</ul>
<p>This makes it perfect for GitHub Pages. GitHub Pages can host static sites for free, and it’s very beginner friendly.</p>
<h3 id="heading-preparing-the-frontend-for-deployment">Preparing the Frontend for Deployment</h3>
<p>Make sure all your frontend files are inside the frontend folder:</p>
<pre><code class="lang-javascript">frontend/
  index.html
  styles.css
  app.js
</code></pre>
<p>Open app.js and make sure the backend URL is the Cloud Run URL, not localhost.</p>
<pre><code class="lang-javascript">fetch(<span class="hljs-string">"https://YOUR_CLOUD_RUN_URL/remove-bg"</span>, {
  <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
  <span class="hljs-attr">body</span>: formData,
});
</code></pre>
<p>Save the file.</p>
<h3 id="heading-pushing-the-frontend-to-github">Pushing the Frontend to GitHub</h3>
<p>We already created a GitHub repository earlier, so we’ll reuse it.</p>
<p>From the project root, run:</p>
<pre><code class="lang-javascript">git add .
git commit -m <span class="hljs-string">"Add frontend and prepare for GitHub Pages"</span>
git push
</code></pre>
<h3 id="heading-enabling-github-pages">Enabling GitHub Pages</h3>
<ol>
<li><p>Go to your repository on GitHub.</p>
</li>
<li><p>Open Settings.</p>
</li>
<li><p>Click Pages.</p>
</li>
</ol>
<p>Under Source, select:</p>
<ol>
<li><p>Branch: main</p>
</li>
<li><p>Folder: /(root)</p>
</li>
</ol>
<p>Save everything. After a few seconds, GitHub will give you a URL. This URL is now your frontend on the internet.</p>
<h3 id="heading-updating-cors-for-the-live-frontend">Updating CORS for the Live Frontend</h3>
<p>Now that the frontend is live, go back to backend/api/main.py.</p>
<p>Replace the local origins with your GitHub Pages URL:</p>
<pre><code class="lang-python">allow_origins=[
    <span class="hljs-string">"https://YOUR_GITHUB_USERNAME.github.io"</span>
]
</code></pre>
<p>Commit and push the change:</p>
<pre><code class="lang-bash">git add .
git commit -m <span class="hljs-string">"Update CORS for GitHub Pages"</span>
git push
</code></pre>
<p>Cloud Run will redeploy the backend automatically.</p>
<h3 id="heading-final-test">Final Test</h3>
<p>Open your GitHub Pages URL.</p>
<p>Upload an image, remove the background, and download the result.</p>
<p>Everything is now live.</p>
<h2 id="heading-where-you-are-now">Where You Are Now</h2>
<p>Let’s be very clear about what you just did.</p>
<p>You:</p>
<ul>
<li><p>Built a real backend</p>
</li>
<li><p>Deployed it to the internet</p>
</li>
<li><p>Built a frontend</p>
</li>
<li><p>Deployed it to the internet</p>
</li>
<li><p>Fixed real production issues</p>
</li>
<li><p>Connected everything properly</p>
</li>
</ul>
<p>This is not a demo project. This is a real application.</p>
<p>In the final section, we’ll wrap things up, talk about what you learned, and where you can go next.</p>
<h2 id="heading-final-thoughts-what-you-just-built-matters">Final Thoughts: What You Just Built Matters</h2>
<p>At this point, it is worth stopping and looking back at what you have actually done.</p>
<p>You didn’t just follow steps. You didn’t just copy code. You built a real application.</p>
<h3 id="heading-lets-be-clear-about-what-you-accomplished">Let’s Be Clear About What You Accomplished</h3>
<p>You started with nothing more than basic tools and ideas.</p>
<p>By the end of this tutorial, you:</p>
<ul>
<li><p>Built a backend that processes real data</p>
</li>
<li><p>Used a machine learning tool without fear or overthinking</p>
</li>
<li><p>Exposed a backend to the internet</p>
</li>
<li><p>Built a frontend with plain HTML, CSS, and JavaScript</p>
</li>
<li><p>Connected the frontend and backend properly</p>
</li>
<li><p>Deployed both parts so real users can access them</p>
</li>
</ul>
<p>This is exactly how real applications are built, just at a smaller and more manageable scale.</p>
<h3 id="heading-why-this-is-no-longer-a-beginner-project">Why This Is No Longer a “Beginner Project”</h3>
<p>Many projects are called beginner projects, but they stop at showing things on a screen.</p>
<p>This one does not.</p>
<p>Your app:</p>
<ul>
<li><p>Accepts real input</p>
</li>
<li><p>Performs real work</p>
</li>
<li><p>Runs on real servers</p>
</li>
<li><p>Handles real browser rules</p>
</li>
<li><p>Can be shared with anyone</p>
</li>
</ul>
<p>That is the difference between learning syntax and building software.</p>
<h3 id="heading-the-most-important-lesson-in-this-tutorial">The Most Important Lesson in This Tutorial</h3>
<p>The most important thing you should take away from this is not the project you built.</p>
<p>It is this: You didn’t need to “know more” before you started.</p>
<p>You learned by building. You figured things out as they appeared. You fixed problems when they showed up.</p>
<p>That is how experience is gained. No one has experience before building real applications. No one lacks experience after building many of them.</p>
<h3 id="heading-what-you-can-do-next">What You Can Do Next</h3>
<p>This project isn’t the end. It’s a starting point.</p>
<p>Here are a few ideas you can explore next, using what you already know:</p>
<ul>
<li><p>Improve the user interface</p>
</li>
<li><p>Add loading states and better feedback</p>
</li>
<li><p>Restrict image size or file types</p>
</li>
<li><p>Add simple rate limiting</p>
</li>
<li><p>Build another small tool that solves a real problem</p>
</li>
</ul>
<p>You don’t need to jump to frameworks yet.</p>
<p>If you can build a few more projects like this, frameworks will make a lot more sense when you meet them.</p>
<h3 id="heading-one-last-thought">One Last Thought</h3>
<p>If this was your first real project, you should be proud of yourself.</p>
<p>You moved past tutorials, built something useful, and put it on the internet. That’s the line many developers never cross.</p>
<p>Now that you have crossed it, the next one will be easier.</p>
<p>So keep building!</p>
<h3 id="heading-source-code-and-live-demo">Source Code and Live Demo</h3>
<p>If you want to explore the full project or build on top of it, you can find everything here.</p>
<ul>
<li><p>Live application: <a target="_blank" href="https://iamspruce.github.io/background-remover/">https://iamspruce.github.io/background-remover/</a></p>
</li>
<li><p>GitHub repository: <a target="_blank" href="https://github.com/iamspruce/background-remover">https://github.com/iamspruce/background-remover</a></p>
</li>
</ul>
<p>If you have questions, reach me on X at <a target="_blank" href="https://x.com/sprucekhalifa"><code>@sprucekhalifa</code></a>. I write practical tech articles like this regularly.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Handle Permissions in Flutter: A Comprehensive Guide ]]>
                </title>
                <description>
                    <![CDATA[ Permissions are crucial when building mobile applications that require access to device features such as location, camera, contacts, microphone, storage, and more. And handling permissions effectively ensures that your app provides a seamless user ex... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-handle-permissions-in-flutter-for-beginners/</link>
                <guid isPermaLink="false">68af2b5d6d32776366258694</guid>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ flutter-aware ]]>
                    </category>
                
                    <category>
                        <![CDATA[ permissions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Atuoha Anthony ]]>
                </dc:creator>
                <pubDate>Wed, 27 Aug 2025 15:59:25 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1756310343452/8db020d5-5cec-4b88-9a02-a8dc2a81190c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Permissions are crucial when building mobile applications that require access to device features such as location, camera, contacts, microphone, storage, and more. And handling permissions effectively ensures that your app provides a seamless user experience while respecting privacy and security requirements.</p>
<p>In Flutter, one of the most popular packages to manage permissions is <a target="_blank" href="https://pub.dev/packages/permission_handler"><code>permission_handler</code></a>. This article will guide you through how to:</p>
<ol>
<li><p>Install and set up <code>permission_handler</code> and <code>fluttertoast</code></p>
</li>
<li><p>Request and handle different permissions</p>
</li>
<li><p>Understand what each permission does and its use cases</p>
</li>
<li><p>Handling Android and iOS configurations</p>
</li>
<li><p>Implement best practices</p>
</li>
<li><p>Handle testing permissions</p>
</li>
<li><p>Provide expected outcomes and conclusions</p>
</li>
</ol>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-table-of-contents">Table of Contents:</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-1-prerequisites">1. Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-installing-dependencies">2. Installing Dependencies</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-understanding-permission-states">3. Understanding Permission States</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-reusable-permission-handling-function">4. Reusable Permission Handling Function</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-5-permissions-their-use-cases-and-examples">5. Permissions, Their Use Cases, and Examples</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-51-calendar-permissions">5.1 Calendar Permissions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-52-camera-permission">5.2 Camera Permission</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-53-contacts-permission">5.3 Contacts Permission</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-54-location-permissions">5.4 Location Permissions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-55-media-library-ios-only">5.5 Media Library (iOS only)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-56-microphone-permission">5.6 Microphone Permission</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-57-phone-permission">5.7 Phone Permission</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-58-photos-permissions">5.8 Photos Permissions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-59-reminders-permission">5.9 Reminders Permission</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-510-sensors-permissions">5.10 Sensors Permissions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-511-sms-permission">5.11 SMS Permission</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-512-speech-recognition-permission">5.12 Speech Recognition Permission</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-513-storage-permissions">5.13 Storage Permissions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-514-ignore-battery-optimizations">5.14 Ignore Battery Optimizations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-515-notifications">5.15 Notifications</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-516-bluetooth-permissions">5.16 Bluetooth Permissions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-517-app-tracking-transparency-ios-only">5.17 App Tracking Transparency (iOS only)</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-6-android-manifest-configuration">6. Android Manifest Configuration</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-7-ios-infoplist-configuration">7. iOS Info.plist Configuration</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-8-expected-outcomes">8. Expected Outcomes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-best-practices-for-handling-permissions-in-flutter">Best Practices for Handling Permissions in Flutter</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-request-only-necessary-permissions">1. Request only necessary permissions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-explain-why-permissions-are-needed">2. Explain why permissions are needed</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-use-runtime-permission-requests">3. Use runtime permission requests</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-handle-denial-gracefully">4. Handle denial gracefully</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-5-handle-permanent-denials">5. Handle permanent denials</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-6-test-on-both-platforms">6. Test on both platforms</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-7-follow-platform-guidelines">7. Follow platform guidelines</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-8-avoid-over-permissioning">8. Avoid over-permissioning</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-9-use-a-centralized-permission-manager">9. Use a centralized permission manager</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-10-monitor-permission-changes">10. Monitor permission changes</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-references">References</a></p>
</li>
</ul>
<h2 id="heading-1-prerequisites">1. Prerequisites</h2>
<p>Before starting, ensure you have the following:</p>
<ol>
<li><p>Flutter SDK installed (version 3.0.0 or higher recommended)</p>
</li>
<li><p>A code editor such as Android Studio or VS Code</p>
</li>
<li><p>Basic understanding of Flutter widgets, async/await in Dart, and state management</p>
</li>
<li><p>A physical device (recommended) or emulator/simulator</p>
</li>
<li><p>Internet connection to install dependencies</p>
</li>
</ol>
<h2 id="heading-2-installing-dependencies">2. Installing Dependencies</h2>
<p>To get started, add the following to your <code>pubspec.yaml</code> file:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">permission_handler:</span> <span class="hljs-string">^11.3.1</span>
  <span class="hljs-attr">fluttertoast:</span> <span class="hljs-string">^8.2.4</span>
</code></pre>
<p>Then run:</p>
<pre><code class="lang-bash">flutter pub get
</code></pre>
<ul>
<li><p><code>permission_handler</code> is used to request and check permissions on Android and iOS.</p>
</li>
<li><p><code>fluttertoast</code> allows you to display messages to users when permissions are granted or denied.</p>
</li>
</ul>
<h2 id="heading-3-understanding-permission-states">3. Understanding Permission States</h2>
<p>When requesting permissions with <code>permission_handler</code>, you can get the following states:</p>
<ol>
<li><p><code>isGranted</code> – The permission is granted.</p>
</li>
<li><p><code>isDenied</code> – The permission is denied, but can be requested again.</p>
</li>
<li><p><code>isPermanentlyDenied</code> – The permission is permanently denied, meaning the user must enable it from <strong>app settings</strong>.</p>
</li>
<li><p><code>isRestricted</code> – The permission is restricted by the system (common on iOS).</p>
</li>
<li><p><code>isLimited</code> – Partial access granted (mainly iOS photo library).</p>
</li>
</ol>
<h2 id="heading-4-reusable-permission-handling-function">4. Reusable Permission Handling Function</h2>
<p>Instead of writing multiple functions for each permission, we’ll create a reusable function:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:permission_handler/permission_handler.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:fluttertoast/fluttertoast.dart'</span>;

Future&lt;<span class="hljs-keyword">void</span>&gt; handlePermission(Permission permission, <span class="hljs-built_in">String</span> name) <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">var</span> status = <span class="hljs-keyword">await</span> permission.request();

  <span class="hljs-keyword">if</span> (status.isGranted) {
    Fluttertoast.showToast(msg: <span class="hljs-string">'<span class="hljs-subst">$name</span> permission granted'</span>);
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (status.isPermanentlyDenied) {
    Fluttertoast.showToast(msg: <span class="hljs-string">'<span class="hljs-subst">$name</span> permission permanently denied. Enable it in settings.'</span>);
    openAppSettings();
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (status.isRestricted) {
    Fluttertoast.showToast(msg: <span class="hljs-string">'<span class="hljs-subst">$name</span> permission restricted by system.'</span>);
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (status.isLimited) {
    Fluttertoast.showToast(msg: <span class="hljs-string">'<span class="hljs-subst">$name</span> permission limited access granted.'</span>);
  } <span class="hljs-keyword">else</span> {
    Fluttertoast.showToast(msg: <span class="hljs-string">'<span class="hljs-subst">$name</span> permission denied'</span>);
  }
}
</code></pre>
<p>Usage example:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.camera, <span class="hljs-string">"Camera"</span>);
<span class="hljs-keyword">await</span> handlePermission(Permission.location, <span class="hljs-string">"Location"</span>);
</code></pre>
<h2 id="heading-5-permissions-their-use-cases-and-examples">5. Permissions, Their Use Cases, and Examples</h2>
<p>Let’s now look at a bunch of different types of permissions you might have to enable in your Flutter apps. I’ll explain what the permission does and its common use cases as well.</p>
<h3 id="heading-51-calendar-permissions">5.1 Calendar Permissions</h3>
<ul>
<li><p><strong>Permission:</strong> <code>calendar</code>, <code>calendarReadOnly</code>, <code>calendarFullAccess</code></p>
</li>
<li><p><strong>What it does:</strong> Accesses the user’s calendar to read or write events.</p>
</li>
<li><p><strong>Use case:</strong> Event apps, scheduling apps.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.calendar, <span class="hljs-string">"Calendar"</span>);
</code></pre>
<h3 id="heading-52-camera-permission">5.2 Camera Permission</h3>
<ul>
<li><p><strong>Permission:</strong> <code>camera</code></p>
</li>
<li><p><strong>What it does:</strong> Access device camera for capturing photos/videos.</p>
</li>
<li><p><strong>Use case:</strong> QR scanning, photo apps, video recording.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.camera, <span class="hljs-string">"Camera"</span>);
</code></pre>
<h3 id="heading-53-contacts-permission">5.3 Contacts Permission</h3>
<ul>
<li><p><strong>Permission:</strong> <code>contacts</code></p>
</li>
<li><p><strong>What it does:</strong> Read or modify user contacts.</p>
</li>
<li><p><strong>Use case:</strong> Messaging apps, social networking apps.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.contacts, <span class="hljs-string">"Contacts"</span>);
</code></pre>
<h3 id="heading-54-location-permissions">5.4 Location Permissions</h3>
<ul>
<li><p><strong>Permission:</strong> <code>location</code>, <code>locationAlways</code>, <code>locationWhenInUse</code></p>
</li>
<li><p><strong>What it does:</strong> Access user’s location.</p>
</li>
<li><p><strong>Use case:</strong> Navigation apps, ride-hailing apps, geofencing.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.locationWhenInUse, <span class="hljs-string">"Location"</span>);
</code></pre>
<h3 id="heading-55-media-library-ios-only">5.5 Media Library (iOS only)</h3>
<ul>
<li><p><strong>Permission:</strong> <code>mediaLibrary</code></p>
</li>
<li><p><strong>What it does:</strong> Access media files on iOS devices.</p>
</li>
<li><p><strong>Use case:</strong> Photo sharing apps, media editors.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.mediaLibrary, <span class="hljs-string">"Media Library"</span>);
</code></pre>
<h3 id="heading-56-microphone-permission">5.6 Microphone Permission</h3>
<ul>
<li><p><strong>Permission:</strong> <code>microphone</code></p>
</li>
<li><p><strong>What it does:</strong> Record audio.</p>
</li>
<li><p><strong>Use case:</strong> Voice notes, video calls, voice commands.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.microphone, <span class="hljs-string">"Microphone"</span>);
</code></pre>
<h3 id="heading-57-phone-permission">5.7 Phone Permission</h3>
<ul>
<li><p><strong>Permission:</strong> <code>phone</code></p>
</li>
<li><p><strong>What it does:</strong> Access phone state, make calls, read call logs.</p>
</li>
<li><p><strong>Use case:</strong> Telephony apps, call management apps.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.phone, <span class="hljs-string">"Phone"</span>);
</code></pre>
<h3 id="heading-58-photos-permissions">5.8 Photos Permissions</h3>
<ul>
<li><p><strong>Permission:</strong> <code>photos</code>, <code>photosAddOnly</code></p>
</li>
<li><p><strong>What it does:</strong> Access or add photos to user library.</p>
</li>
<li><p><strong>Use case:</strong> Media apps, social apps.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.photos, <span class="hljs-string">"Photos"</span>);
</code></pre>
<h3 id="heading-59-reminders-permission">5.9 Reminders Permission</h3>
<ul>
<li><p><strong>Permission:</strong> <code>reminders</code></p>
</li>
<li><p><strong>What it does:</strong> Access and manage device reminders.</p>
</li>
<li><p><strong>Use case:</strong> To-do apps, productivity apps.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.reminders, <span class="hljs-string">"Reminders"</span>);
</code></pre>
<h3 id="heading-510-sensors-permissions">5.10 Sensors Permissions</h3>
<ul>
<li><p><strong>Permission:</strong> <code>sensors</code>, <code>sensorsAlways</code></p>
</li>
<li><p><strong>What it does:</strong> Access device sensors like accelerometer or gyroscope.</p>
</li>
<li><p><strong>Use case:</strong> Fitness apps, motion tracking apps.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.sensors, <span class="hljs-string">"Sensors"</span>);
</code></pre>
<h3 id="heading-511-sms-permission">5.11 SMS Permission</h3>
<ul>
<li><p><strong>Permission:</strong> <code>sms</code></p>
</li>
<li><p><strong>What it does:</strong> Read or send SMS messages.</p>
</li>
<li><p><strong>Use case:</strong> OTP verification, messaging apps.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.sms, <span class="hljs-string">"SMS"</span>);
</code></pre>
<h3 id="heading-512-speech-recognition-permission">5.12 Speech Recognition Permission</h3>
<ul>
<li><p><strong>Permission:</strong> <code>speech</code></p>
</li>
<li><p><strong>What it does:</strong> Use speech-to-text features.</p>
</li>
<li><p><strong>Use case:</strong> Voice commands, dictation apps.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.speech, <span class="hljs-string">"Speech Recognition"</span>);
</code></pre>
<h3 id="heading-513-storage-permissions">5.13 Storage Permissions</h3>
<ul>
<li><p><strong>Permission:</strong> <code>storage</code>, <code>manageExternalStorage</code></p>
</li>
<li><p><strong>What it does:</strong> Access internal/external storage to read/write files.</p>
</li>
<li><p><strong>Use case:</strong> File managers, download managers.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.storage, <span class="hljs-string">"Storage"</span>);
</code></pre>
<h3 id="heading-514-ignore-battery-optimizations">5.14 Ignore Battery Optimizations</h3>
<ul>
<li><p><strong>Permission:</strong> <code>ignoreBatteryOptimizations</code></p>
</li>
<li><p><strong>What it does:</strong> Request to exclude app from battery optimizations.</p>
</li>
<li><p><strong>Use case:</strong> Alarm apps, background services.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.ignoreBatteryOptimizations, <span class="hljs-string">"Battery Optimizations"</span>);
</code></pre>
<h3 id="heading-515-notifications">5.15 Notifications</h3>
<ul>
<li><p><strong>Permission:</strong> <code>notification</code></p>
</li>
<li><p><strong>What it does:</strong> Allow sending notifications.</p>
</li>
<li><p><strong>Use case:</strong> Messaging, reminders, alerts.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.notification, <span class="hljs-string">"Notifications"</span>);
</code></pre>
<h3 id="heading-516-bluetooth-permissions">5.16 Bluetooth Permissions</h3>
<ul>
<li><p><strong>Permission:</strong> <code>bluetooth</code>, <code>bluetoothScan</code>, <code>bluetoothAdvertise</code>, <code>bluetoothConnect</code></p>
</li>
<li><p><strong>What it does:</strong> Manage or connect to Bluetooth devices.</p>
</li>
<li><p><strong>Use case:</strong> Wearables, IoT devices, headphones.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.bluetooth, <span class="hljs-string">"Bluetooth"</span>);
</code></pre>
<h3 id="heading-517-app-tracking-transparency-ios-only">5.17 App Tracking Transparency (iOS only)</h3>
<ul>
<li><p><strong>Permission:</strong> <code>appTrackingTransparency</code></p>
</li>
<li><p><strong>What it does:</strong> Request tracking permission for personalized ads.</p>
</li>
<li><p><strong>Use case:</strong> Analytics, advertising, user tracking.</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">await</span> handlePermission(Permission.appTrackingTransparency, <span class="hljs-string">"App Tracking"</span>);
</code></pre>
<h2 id="heading-6-android-manifest-configuration">6. Android Manifest Configuration</h2>
<p>On <strong>Android</strong>, all apps must declare the permissions they intend to use in the <code>AndroidManifest.xml</code> file. This acts as the app’s “contract” with the system, letting Android know what sensitive resources (like internet, location, camera) the app might request.</p>
<p>Without declaring these in the manifest, runtime permission requests will fail, even if you’ve added the <code>permission_handler</code> package.</p>
<p>For example, if you try to access the camera without first declaring the camera permission here, your app will crash or fail when requesting it at runtime.</p>
<p>Below is a comprehensive list of common permissions:</p>
<pre><code class="lang-xml"><span class="hljs-comment">&lt;!-- Camera access for taking pictures or recording videos --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.CAMERA"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Read user contacts (e.g., for social features, friend finder) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.READ_CONTACTS"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Fine-grained location (GPS) for maps, navigation, geofencing --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.ACCESS_FINE_LOCATION"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Coarse location (network-based, less accurate, uses WiFi/Cell) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.ACCESS_COARSE_LOCATION"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Record audio from the microphone (e.g., voice notes, calls) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.RECORD_AUDIO"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Read external storage (access user’s files like images, docs) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.READ_EXTERNAL_STORAGE"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Write to external storage (save downloaded files, photos) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.WRITE_EXTERNAL_STORAGE"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Send SMS directly from the app --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.SEND_SMS"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Receive SMS (read OTP messages for login/verification) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.RECEIVE_SMS"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Read SMS messages (OTP autofill, chat backup restore) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.READ_SMS"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Bluetooth usage (connect to Bluetooth devices like headsets, printers) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.BLUETOOTH"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Bluetooth Admin (manage paired devices, discover nearby devices) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.BLUETOOTH_ADMIN"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Bluetooth Scan (needed from Android 12+) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.BLUETOOTH_SCAN"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Bluetooth Connect (needed from Android 12+) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.BLUETOOTH_CONNECT"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Bluetooth Advertise (for beacon-style apps, Android 12+) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.BLUETOOTH_ADVERTISE"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Ignore battery optimizations (keep app alive in background tasks) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Internet access (needed for network requests, APIs, file uploads) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.INTERNET"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Check if a network connection exists (WiFi/Mobile data) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.ACCESS_NETWORK_STATE"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Access WiFi state (check SSID, connection info, used for network control) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.ACCESS_WIFI_STATE"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Change WiFi state (enable/disable WiFi programmatically) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.CHANGE_WIFI_STATE"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Access phone state (read device ID, SIM details, call status) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.READ_PHONE_STATE"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Make phone calls directly from the app --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.CALL_PHONE"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Use fingerprint authentication (for biometric login) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.USE_FINGERPRINT"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- Use Biometric authentication (newer than fingerprint, includes face) --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.USE_BIOMETRIC"</span> /&gt;</span>
</code></pre>
<h2 id="heading-7-ios-infoplist-configuration">7. iOS <code>Info.plist</code> Configuration</h2>
<p>For iOS, you must provide descriptive keys in the <code>Info.plist</code> file to inform users why your app needs specific permissions. Below are the configurations for each permission example:</p>
<ol>
<li><p><strong>Camera Permission</strong></p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSCameraUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need access to your camera for taking pictures.<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
</li>
<li><p><strong>Contacts Permission</strong></p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSContactsUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need access to your contacts for better communication.<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
</li>
<li><p><strong>Location Permissions</strong></p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSLocationWhenInUseUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need access to your location to provide location-based services.<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSLocationAlwaysUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need access to your location to track your movement even when the app is not active.<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
</li>
<li><p><strong>Media Library/Storage Permission</strong></p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSPhotoLibraryUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need access to your photos for sharing or uploading media.<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
</li>
<li><p><strong>Microphone Permission</strong></p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSMicrophoneUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need access to your microphone for voice recording.<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
</li>
<li><p><strong>Phone Permission</strong></p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSPhoneUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need access to phone services to enable calls.<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
</li>
<li><p><strong>Photos Permission</strong></p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSPhotoLibraryAddUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need permission to add photos to your library.<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
</li>
<li><p><strong>Reminders Permission</strong></p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSRemindersUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need access to your reminders for task management.<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
</li>
<li><p><strong>Sensors Permission</strong></p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSSensorsUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need access to your sensors for fitness tracking.<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
</li>
<li><p><strong>SMS Permission</strong> (Handled automatically by iOS if SMS services are requested)</p>
</li>
<li><p><strong>Speech Recognition Permission</strong></p>
</li>
</ol>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSSpeechRecognitionUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need access to speech recognition for voice commands.<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
<ol start="12">
<li><p><strong>Ignore Battery Optimizations</strong> (Not applicable on iOS)</p>
</li>
<li><p><strong>Notifications Permission</strong></p>
</li>
</ol>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSUserNotificationUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need permission to send notifications.<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
<ol start="14">
<li><p><strong>Access Media Location Permission</strong> (Handled automatically on iOS)</p>
</li>
<li><p><strong>Activity Recognition Permission</strong></p>
</li>
</ol>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSMotionUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need access to motion data for fitness tracking.<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
<ol start="16">
<li><strong>Bluetooth Permissions</strong></li>
</ol>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSBluetoothPeripheralUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need access to Bluetooth for device connectivity.<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
<ol start="17">
<li><strong>App Tracking Transparency</strong></li>
</ol>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSUserTrackingUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need permission to track your activity across apps and websites for personalized ads.<span class="hljs-tag">&lt;/<span class="hljs-name">st</span></span>
</code></pre>
<h2 id="heading-8-expected-outcomes">8. Expected Outcomes</h2>
<p>By implementing permissions and connectivity checks this way, your Flutter app will:</p>
<ol>
<li><p>Request permissions dynamically at runtime in a user-friendly way.</p>
</li>
<li><p>Handle all possible states gracefully, including granted, denied, permanently denied, restricted, and limited.</p>
</li>
<li><p>Provide users with meaningful feedback, guiding them to settings if necessary.</p>
</li>
<li><p>Maintain compliance with Android and iOS permission policies while ensuring security and transparency.</p>
</li>
<li><p>Listen continuously to network connectivity changes via the global BLoC listener.</p>
</li>
<li><p>Notify users instantly with a toast/snackbar whenever internet status changes (connected/disconnected).</p>
</li>
<li><p>Reduce redundant API calls and improve UX by avoiding “fake” offline or cached-only states.</p>
</li>
</ol>
<h2 id="heading-best-practices-for-handling-permissions-in-flutter">Best Practices for Handling Permissions in Flutter</h2>
<p>There are some common best practices you should follow when handling permissions in Flutter.</p>
<h3 id="heading-1-request-only-necessary-permissions">1. Request only necessary permissions</h3>
<p>Ask only for the permissions your app truly needs. For example, if your app just uploads images, you probably only need <strong>storage/photos access</strong>, not location, contacts, or SMS.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> status = <span class="hljs-keyword">await</span> Permission.photos.request();
<span class="hljs-keyword">if</span> (status.isGranted) {
  <span class="hljs-comment">// Proceed with photo upload</span>
}
</code></pre>
<h3 id="heading-2-explain-why-permissions-are-needed">2. Explain why permissions are needed</h3>
<p>Always tell the user <em>why</em> you’re asking for a sensitive permission before the system dialog appears. This helps build trust.</p>
<p>Example of a custom dialog before requesting:</p>
<pre><code class="lang-dart">Future&lt;<span class="hljs-keyword">void</span>&gt; _showPermissionRationale(BuildContext context) <span class="hljs-keyword">async</span> {
  showDialog(
    context: context,
    builder: (context) =&gt; AlertDialog(
      title: Text(<span class="hljs-string">"Camera Access Needed"</span>),
      content: Text(<span class="hljs-string">"We need access to your camera so you can take profile photos."</span>),
      actions: [
        TextButton(
          onPressed: () {
            Navigator.pop(context);
            Permission.camera.request();
          },
          child: Text(<span class="hljs-string">"Allow"</span>),
        ),
        TextButton(
          onPressed: () =&gt; Navigator.pop(context),
          child: Text(<span class="hljs-string">"Cancel"</span>),
        ),
      ],
    ),
  );
}
</code></pre>
<h3 id="heading-3-use-runtime-permission-requests">3. Use runtime permission requests</h3>
<p>On Android 6.0+ and iOS, permissions must be requested <strong>at runtime</strong> (not just declared in <code>AndroidManifest.xml</code> or <code>Info.plist</code>).</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> status = <span class="hljs-keyword">await</span> Permission.location.request();
<span class="hljs-keyword">if</span> (status.isGranted) {
  <span class="hljs-comment">// Use location</span>
}
</code></pre>
<h3 id="heading-4-handle-denial-gracefully">4. Handle denial gracefully</h3>
<p>Don’t block the entire app when permissions are denied. Provide alternative flows.</p>
<p>For example, instead of forcing camera access:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">if</span> (<span class="hljs-keyword">await</span> Permission.camera.isDenied) {
  <span class="hljs-comment">// Offer file upload as fallback</span>
  _pickImageFromGallery();
}
</code></pre>
<p>This way, users can still use your app without being forced.</p>
<h3 id="heading-5-handle-permanent-denials">5. Handle permanent denials</h3>
<p>When a user selects <em>“Don’t ask again”</em> (Android) or disables a permission in Settings (iOS), you should guide them to <strong>Settings</strong>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">if</span> (<span class="hljs-keyword">await</span> Permission.camera.isPermanentlyDenied) {
  openAppSettings(); <span class="hljs-comment">// Takes user to app settings</span>
}
</code></pre>
<p>UX Example:</p>
<ul>
<li>Show a snackbar: <em>“Camera access is required. Enable it in Settings.”</em> with a <strong>Go to Settings</strong> button.</li>
</ul>
<h3 id="heading-6-test-on-both-platforms">6. Test on both platforms</h3>
<p>Permissions behave differently across Android and iOS. Example:</p>
<ul>
<li><p>iOS may return limited photo library access.</p>
</li>
<li><p>Android 13+ has new granular media permissions (<code>READ_MEDIA_IMAGES</code>, <code>READ_MEDIA_VIDEO</code>).</p>
</li>
</ul>
<p>Always test all scenarios:</p>
<ul>
<li><p>Granted</p>
</li>
<li><p>Denied once</p>
</li>
<li><p>Denied permanently</p>
</li>
<li><p>Limited (iOS only)</p>
</li>
</ul>
<h3 id="heading-7-follow-platform-guidelines">7. Follow platform guidelines</h3>
<p>Make sure your manifest and Info.plist contain clear explanations.</p>
<p><strong>Info.plist example (iOS):</strong></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSCameraUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>This app requires camera access to let you take profile pictures.<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
<p>This is required for App Store approval.</p>
<h3 id="heading-8-avoid-over-permissioning">8. Avoid over-permissioning</h3>
<p>Example: Don’t request SMS if you only need phone number autofill. Users will abandon your app if they see irrelevant requests.</p>
<p><strong>Bad:</strong></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">uses-permission</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.permission.SEND_SMS"</span> /&gt;</span>
</code></pre>
<p>Just use <code>READ_PHONE_NUMBERS</code> if that’s the actual need.</p>
<h3 id="heading-9-use-a-centralized-permission-manager">9. Use a centralized permission manager</h3>
<p>Instead of scattering requests across the app, create a PermissionService that handles all requests consistently.</p>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PermissionService</span> </span>{
  Future&lt;<span class="hljs-built_in">bool</span>&gt; requestCamera() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> status = <span class="hljs-keyword">await</span> Permission.camera.request();
    <span class="hljs-keyword">return</span> status.isGranted;
  }

  Future&lt;<span class="hljs-built_in">bool</span>&gt; requestLocation() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> status = <span class="hljs-keyword">await</span> Permission.location.request();
    <span class="hljs-keyword">return</span> status.isGranted;
  }
}
</code></pre>
<p>This keeps permission handling uniform.</p>
<h3 id="heading-10-monitor-permission-changes">10. Monitor permission changes</h3>
<p>Permissions can change while the app is open (user goes to Settings and disables it). Always check before use.</p>
<pre><code class="lang-dart"><span class="hljs-meta">@override</span>
<span class="hljs-keyword">void</span> initState() {
  <span class="hljs-keyword">super</span>.initState();
  Timer.periodic(<span class="hljs-built_in">Duration</span>(seconds: <span class="hljs-number">5</span>), (timer) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> cameraStatus = <span class="hljs-keyword">await</span> Permission.camera.status;
    <span class="hljs-keyword">if</span> (!cameraStatus.isGranted) {
      <span class="hljs-comment">// Disable camera UI</span>
    }
  });
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Permissions are fundamental for building fully functional and secure mobile applications. Using <code>permission_handler</code> in Flutter allows you to manage permissions across Android and iOS efficiently.</p>
<p>And just remember: always request only necessary permissions, provide clear explanations, and handle all possible states to maintain trust with users.</p>
<p>By combining correct permission logic with proper AndroidManifest and Info.plist setup, you ensure a seamless user experience while staying compliant with platform guidelines.</p>
<h2 id="heading-references">References</h2>
<ol>
<li><p><a target="_blank" href="https://pub.dev/packages/permission_handler">permission_handler Flutter Package Documentation</a></p>
</li>
<li><p><a target="_blank" href="https://docs.flutter.dev/cookbook/plugins/picture-using-camera">Flutter Official Documentation: Handling Permissions</a></p>
</li>
<li><p><a target="_blank" href="https://developer.android.com/guide/topics/permissions/overview">Android Developer Guide: Permissions</a></p>
</li>
<li><p><a target="_blank" href="https://developer.apple.com/documentation/bundleresources/information_property_list">iOS Developer Guide: App Permissions</a></p>
</li>
<li><p><a target="_blank" href="https://pub.dev/packages/fluttertoast">Fluttertoast Package Documentation</a></p>
</li>
</ol>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Fix the Python ENOENT Error When Setting Up MCP Servers – A Complete Guide ]]>
                </title>
                <description>
                    <![CDATA[ Getting the "spawn python ENOENT" error while setting up an MCP (Model Context Protocol) server on macOS can be frustrating. But don't worry – in this tutorial, I'll guide you through fixing it by rebuilding your Python virtual environment. By the en... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-fix-the-python-enoent-error-when-setting-up-mcp-servers-a-complete-guide/</link>
                <guid isPermaLink="false">68963890790ac4491c15b00a</guid>
                
                    <category>
                        <![CDATA[ mcp server ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Blockchain ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Developer ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ macOS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Idris Olubisi ]]>
                </dc:creator>
                <pubDate>Fri, 08 Aug 2025 17:49:04 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1754675334533/6a05e45a-9703-49c0-b427-6c4960c01d86.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Getting the "spawn python ENOENT" error while setting up an MCP (Model Context Protocol) server on macOS can be frustrating. But don't worry – in this tutorial, I'll guide you through fixing it by rebuilding your Python virtual environment.</p>
<p>By the end, you'll have a fully functional MCP server integrated with Claude Desktop in about 10 minutes. This solution applies to any MCP setup facing this standard error after Python upgrades.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-causes-the-enoent-error">What Causes the ENOENT Error?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-diagnose-your-broken-virtual-environment">How to Diagnose Your Broken Virtual Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-completely-rebuild-your-virtual-environment">How to Completely Rebuild Your Virtual Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-install-mcp-server-dependencies">How to Install MCP Server Dependencies</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-locate-your-server-files">How to Locate Your Server Files</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-test-your-server-setup">How to Test Your Server Setup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-configure-claude-desktop">How to Configure Claude Desktop</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-restart-claude-desktop-and-test-integration">How to Restart Claude Desktop and Test Integration</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-understanding-mcp-server-capabilities">Understanding MCP Server Capabilities</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-alternative-installation-methods">Alternative Installation Methods</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-method-1-direct-package-installation">Method 1: Direct Package Installation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-method-2-using-uv-package-manager">Method 2: Using UV Package Manager</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-prevent-future-enoent-errors">How to Prevent Future ENOENT Errors</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-troubleshooting-common-issues">Troubleshooting Common Issues</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-what-causes-the-enoent-error">What Causes the ENOENT Error?</h2>
<p>The ENOENT (Error NO ENTry) error means your system can’t locate the Python executable at the specified path. This occurs when the file is missing or inaccessible.</p>
<p>On macOS, this typically happens when:</p>
<ul>
<li><p>You've upgraded Python through Homebrew</p>
</li>
<li><p>The <code>brew cleanup</code> command removed old Python versions</p>
</li>
<li><p>Your virtual environment's symlinks now point to non-existent files</p>
</li>
</ul>
<p>What makes this particularly challenging is that your virtual environment folder still exists – it looks fine from the outside, but the Python executable inside is completely broken.</p>
<p>When MCP servers try to spawn Python processes using these broken paths, you get the dreaded ENOENT error. This affects any Python-based MCP server, whether you're building custom tools, connecting to APIs, or working with file systems.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow this tutorial, you'll need:</p>
<ul>
<li><p>macOS with <a target="_blank" href="https://brew.sh/">Homebrew</a> installed</p>
</li>
<li><p>Python 3.10 or higher</p>
</li>
<li><p>An MCP server repository cloned locally</p>
</li>
<li><p><a target="_blank" href="https://claude.ai/download">Claude Desktop</a> installed</p>
</li>
<li><p>Basic familiarity with terminal commands and Python virtual environments</p>
</li>
</ul>
<p>If you haven't cloned an MCP server repository yet, you can start with any open-source MCP server. For this tutorial, I'll use generic examples that work with any MCP setup:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/your-username/your-mcp-server.git
<span class="hljs-built_in">cd</span> your-mcp-server
</code></pre>
<h2 id="heading-how-to-diagnose-your-broken-virtual-environment">How to Diagnose Your Broken Virtual Environment</h2>
<p>First, you need to confirm that your virtual environment is actually the problem. Open your terminal and navigate to your MCP directory:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> /path/to/your/mcp-server
</code></pre>
<p>Now check if your Python executable exists:</p>
<pre><code class="lang-bash">ls -la venv/bin/python*
</code></pre>
<p>If you see broken symlinks or get "No such file or directory" errors, you've found your problem. You might see output like:</p>
<pre><code class="lang-bash">lrwxr-xr-x  1 username  staff  16 Jan  1 12:00 python -&gt; /usr/<span class="hljs-built_in">local</span>/bin/python3.11
lrwxr-xr-x  1 username  staff  16 Jan  1 12:00 python3 -&gt; /usr/<span class="hljs-built_in">local</span>/bin/python3.11
</code></pre>
<p>But when you try to run these Python executables:</p>
<pre><code class="lang-bash">./venv/bin/python --version
</code></pre>
<p>You'll get an error because the target files no longer exist. This confirms your virtual environment is broken and needs rebuilding.</p>
<h2 id="heading-how-to-completely-rebuild-your-virtual-environment">How to Completely Rebuild Your Virtual Environment</h2>
<p>The most reliable solution is to rebuild your virtual environment from scratch. This ensures all paths and dependencies are correctly configured for your current Python installation.</p>
<p>Here's your step-by-step rebuild process:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Make sure you're in the MCP server directory</span>
<span class="hljs-built_in">cd</span> /path/to/your/mcp-server

<span class="hljs-comment"># Remove the corrupted virtual environment</span>
rm -rf venv

<span class="hljs-comment"># Create a fresh virtual environment</span>
python3 -m venv venv

<span class="hljs-comment"># Activate the new environment</span>
<span class="hljs-built_in">source</span> venv/bin/activate
</code></pre>
<p>You should now see <code>(venv)</code> in your terminal prompt, indicating the virtual environment is active. This prefix confirms you're working within the isolated Python environment.</p>
<h2 id="heading-how-to-install-mcp-server-dependencies">How to Install MCP Server Dependencies</h2>
<p>With your fresh virtual environment active, install the MCP server and its dependencies. The exact installation command depends on your specific MCP server, but typically follows one of these patterns:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># For package-based installation</span>
pip install -e .

<span class="hljs-comment"># Or for requirements file</span>
pip install -r requirements.txt

<span class="hljs-comment"># Or for specific MCP frameworks</span>
pip install fastmcp
</code></pre>
<p>Common MCP server dependencies include:</p>
<ul>
<li><p>FastMCP for the server framework</p>
</li>
<li><p>JSON-RPC libraries for communication protocols</p>
</li>
<li><p>HTTP clients for API integrations</p>
</li>
<li><p>File system utilities for local operations</p>
</li>
</ul>
<p>The installation process displays all packages as they install. Don't worry if you see deprecation warnings – they're normal and won't affect functionality.</p>
<h2 id="heading-how-to-locate-your-server-files">How to Locate Your Server Files</h2>
<p>After installation, identify where your main server file lives. Run this command to find all server.py files:</p>
<pre><code class="lang-bash">find . -name <span class="hljs-string">"server.py"</span> -<span class="hljs-built_in">type</span> f
</code></pre>
<p>You may see results like:</p>
<ul>
<li><p><code>./server.py</code> (in the root directory)</p>
</li>
<li><p><code>./src/server.py</code> (in a source directory)</p>
</li>
<li><p><code>./mcp_server/server.py</code> (in a package directory)</p>
</li>
</ul>
<p>Check your current directory structure:</p>
<pre><code class="lang-bash">ls -la
</code></pre>
<p>Look for the main server entry point. Most MCP servers follow standard Python project structures with either a root-level server file or one nested in a package directory.</p>
<h2 id="heading-how-to-test-your-server-setup">How to Test Your Server Setup</h2>
<p>Now you’ll want to test your server to ensure it's working correctly. Start with the main server file you identified:</p>
<pre><code class="lang-bash">python server.py
</code></pre>
<p>If this is the correct server and everything is configured correctly, you'll see output similar to:</p>
<pre><code class="lang-typescript">╭─ MCP Server ───────────────────────────────────────────────────────────────╮
│ 🖥️  Server name: Example-MCP                                              │
│ 📦 Transport: STDIO                                                        │
│ 🤝 Protocol: <span class="hljs-built_in">JSON</span>-RPC                                                      │
╰────────────────────────────────────────────────────────────────────────────╯
[INFO] Starting MCP server <span class="hljs-keyword">with</span> transport <span class="hljs-string">'stdio'</span>
[INFO] Server ready <span class="hljs-keyword">for</span> connections
</code></pre>
<p>This output confirms your MCP server is working correctly. The server uses standard input/output (STDIO) for communication, which is perfect for Claude Desktop integration. You can stop the server with <code>Ctrl+C</code>.</p>
<h2 id="heading-how-to-configure-claude-desktop">How to Configure Claude Desktop</h2>
<p>Now that your server runs properly, configure Claude Desktop to connect to it. The configuration file location depends on your operating system:</p>
<p><strong>For macOS:</strong></p>
<pre><code class="lang-bash">~/Library/Application Support/Claude/claude_desktop_config.json
</code></pre>
<p><strong>For Windows:</strong></p>
<pre><code class="lang-bash">%APPDATA%\Claude\claude_desktop_config.json
</code></pre>
<p><strong>For Linux:</strong></p>
<pre><code class="lang-bash">~/.config/Claude/claude_desktop_config.json
</code></pre>
<p>Create or edit this file with your exact paths. Your configuration should look like this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"mcpServers"</span>: {
    <span class="hljs-attr">"example-mcp"</span>: {
      <span class="hljs-attr">"command"</span>: <span class="hljs-string">"/Users/yourusername/path/to/mcp-server/venv/bin/python"</span>,
      <span class="hljs-attr">"args"</span>: [<span class="hljs-string">"/Users/yourusername/path/to/mcp-server/server.py"</span>],
      <span class="hljs-attr">"cwd"</span>: <span class="hljs-string">"/Users/yourusername/path/to/mcp-server"</span>
    }
  }
}
</code></pre>
<p>Replace <code>/Users/yourusername/path/to/mcp-server/</code> with your actual path. You can get your precise path by running <code>pwd</code> in your MCP server directory.</p>
<p>The configuration tells Claude Desktop:</p>
<ul>
<li><p>Which Python interpreter to use (from your virtual environment)</p>
</li>
<li><p>Where to find the server script</p>
</li>
<li><p>Which directory to run the server from</p>
</li>
</ul>
<h2 id="heading-how-to-restart-claude-desktop-and-test-integration">How to Restart Claude Desktop and Test Integration</h2>
<p>After saving your configuration file, altogether quit Claude Desktop (not just close the window). On macOS, use <code>Cmd+Q</code> or right-click the dock icon and select Quit. Then restart Claude Desktop.</p>
<p>Once Claude Desktop is running again, test your MCP integration. You can verify the connection by:</p>
<ol>
<li><p>Looking for your MCP server name in Claude's interface</p>
</li>
<li><p>Testing basic MCP functionality with prompts like:</p>
<ul>
<li><p>"What MCP tools are available?"</p>
</li>
<li><p>"Can you check the MCP server status?"</p>
</li>
<li><p>"Show me the available MCP commands"</p>
</li>
</ul>
</li>
</ol>
<p>If everything is working correctly, Claude will respond using the MCP server tools, confirming successful integration.</p>
<h2 id="heading-understanding-mcp-server-capabilities">Understanding MCP Server Capabilities</h2>
<p>MCP servers extend Claude's capabilities by providing structured access to external tools and data sources. Common MCP server implementations include:</p>
<ol>
<li><p>File system operations: MCP servers can provide controlled access to local files, allowing Claude to read, analyze, and process documents while maintaining security boundaries.</p>
</li>
<li><p>API integrations: Connect Claude to external services through MCP servers that handle authentication, rate limiting, and data formatting for various APIs.</p>
</li>
<li><p>Database connections: Query databases safely through MCP servers that manage connections, handle credentials securely, and format results for Claude's consumption.</p>
</li>
<li><p>Custom tools: Build specialized tools for your workflow, from code analysis to data processing, all accessible through the standardized MCP interface.</p>
</li>
</ol>
<p>The beauty of MCP is its flexibility – you can create servers for virtually any tool or service you need Claude to interact with.</p>
<h2 id="heading-alternative-installation-methods">Alternative Installation Methods</h2>
<p>If you want more streamlined approaches for future setups, here are two excellent alternatives:</p>
<h3 id="heading-method-1-direct-package-installation">Method 1: Direct Package Installation</h3>
<p>For MCP servers available as packages, you can install directly:</p>
<pre><code class="lang-bash">pip install mcp-server-package
</code></pre>
<p>Then use this simpler configuration:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"mcpServers"</span>: {
    <span class="hljs-attr">"example-mcp"</span>: {
      <span class="hljs-attr">"command"</span>: <span class="hljs-string">"mcp-server-command"</span>
    }
  }
}
</code></pre>
<p>This method works when the MCP server provides a command-line entry point through its setup configuration.</p>
<h3 id="heading-method-2-using-uv-package-manager">Method 2: Using UV Package Manager</h3>
<p>UV provides more robust dependency management – perfect if you're tired of Python version conflicts:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Install UV</span>
curl -LsSf https://astral.sh/uv/install.sh | sh

<span class="hljs-comment"># Use UV in your configuration</span>
{
  <span class="hljs-string">"mcpServers"</span>: {
    <span class="hljs-string">"example-mcp"</span>: {
      <span class="hljs-string">"command"</span>: <span class="hljs-string">"uv"</span>,
      <span class="hljs-string">"args"</span>: [
        <span class="hljs-string">"run"</span>,
        <span class="hljs-string">"--with"</span>, <span class="hljs-string">"fastmcp"</span>,
        <span class="hljs-string">"python"</span>,
        <span class="hljs-string">"/path/to/mcp-server/server.py"</span>
      ],
      <span class="hljs-string">"cwd"</span>: <span class="hljs-string">"/path/to/mcp-server"</span>
    }
  }
}
</code></pre>
<p>UV automatically manages Python versions and dependencies, reducing the likelihood of environment-related errors.</p>
<h2 id="heading-how-to-prevent-future-enoent-errors">How to Prevent Future ENOENT Errors</h2>
<p>To avoid this issue in the future, follow these best practices:</p>
<h3 id="heading-1-use-virtual-environment-copies-instead-of-symlinks">1. Use Virtual Environment Copies Instead of Symlinks</h3>
<p>When creating virtual environments, use the <code>--copies</code> flag:</p>
<pre><code class="lang-bash">python3 -m venv venv --copies
</code></pre>
<p>This creates actual copies of files instead of symlinks, making your environment more resilient to Python upgrades.</p>
<h3 id="heading-2-pin-your-homebrew-python-version">2. Pin Your Homebrew Python Version</h3>
<p>Prevent automatic Python upgrades that break environments:</p>
<pre><code class="lang-bash">brew pin python@3.11
</code></pre>
<p>Remember to unpin when you're ready to upgrade intentionally.</p>
<h3 id="heading-3-create-a-health-check-script">3. Create a Health Check Script</h3>
<p>Save this script as <code>health_check.sh</code> in your MCP server directory:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-comment"># health_check.sh</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"Checking Python virtual environment..."</span>
<span class="hljs-built_in">source</span> venv/bin/activate

python -c <span class="hljs-string">"import sys; print(f'Python: {sys.executable}')"</span>
python -c <span class="hljs-string">"print('✓ Python is working')"</span>

<span class="hljs-comment"># Check for common MCP dependencies</span>
python -c <span class="hljs-string">"import json; print('✓ JSON module available')"</span>
python -c <span class="hljs-string">"import asyncio; print('✓ Asyncio available')"</span>

<span class="hljs-built_in">echo</span> <span class="hljs-string">"Health check complete!"</span>
</code></pre>
<p>Make it executable and run it periodically:</p>
<pre><code class="lang-bash">chmod +x health_check.sh
./health_check.sh
</code></pre>
<h3 id="heading-4-document-your-python-version">4. Document Your Python Version</h3>
<p>Create a <code>.python-version</code> file in your project:</p>
<pre><code class="lang-bash">python --version &gt; .python-version
</code></pre>
<p>This helps you remember which Python version the project was built with.</p>
<h2 id="heading-troubleshooting-common-issues">Troubleshooting Common Issues</h2>
<p>Even with the fix applied, you might encounter these challenges:</p>
<h3 id="heading-import-errors">Import Errors</h3>
<p>If you see import-related errors, ensure all dependencies are installed:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> venv/bin/activate
pip list  <span class="hljs-comment"># Check installed packages</span>
pip install -r requirements.txt  <span class="hljs-comment"># Reinstall if needed</span>
</code></pre>
<h3 id="heading-permission-denied-errors">Permission Denied Errors</h3>
<p>Make sure your server file is executable:</p>
<pre><code class="lang-bash">chmod +x server.py
</code></pre>
<h3 id="heading-claude-desktop-not-finding-the-server">Claude Desktop Not Finding the Server</h3>
<p>Double-check your configuration paths are absolute, not relative:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Good - absolute path</span>
<span class="hljs-string">"/Users/username/projects/mcp-server/server.py"</span>

<span class="hljs-comment"># Bad - relative path</span>
<span class="hljs-string">"./server.py"</span>
</code></pre>
<h3 id="heading-server-starts-but-claude-cant-connect">Server Starts, But Claude Can't Connect</h3>
<p>Verify that the transport method matches between your server and the configuration. Most MCP servers use STDIO, but some might use HTTP or WebSocket transports.</p>
<h3 id="heading-multiple-python-installations">Multiple Python Installations</h3>
<p>If you have multiple Python versions, be explicit about which one to use:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Check available Python versions</span>
ls -la /usr/<span class="hljs-built_in">local</span>/bin/python*

<span class="hljs-comment"># Use a specific version</span>
/usr/<span class="hljs-built_in">local</span>/bin/python3.11 -m venv venv
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You've successfully fixed the "spawn python ENOENT" error by rebuilding your Python virtual environment and properly configuring your MCP server for Claude Desktop. You've also learned how to prevent future mistakes and troubleshoot common issues.</p>
<p>With your MCP server running smoothly, you can now:</p>
<ul>
<li><p>Build custom tools that extend Claude's capabilities</p>
</li>
<li><p>Create integrations with your favorite services</p>
</li>
<li><p>Develop specialized workflows for your specific needs</p>
</li>
<li><p>Share your MCP servers with the community</p>
</li>
</ul>
<p>The <a target="_blank" href="https://www.anthropic.com/news/model-context-protocol">MCP</a> ecosystem is growing rapidly, with new servers and tools being developed constantly. Whether you're building file system tools, API integrations, or custom utilities, you now have the foundation to create and maintain robust MCP servers.</p>
<p>Happy building, and enjoy your error-free development journey! For more tutorials, follow my work on <a target="_blank" href="https://github.com/Olanetsoft">GitHub</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ A Beginner Developer's Guide to Kanban ]]>
                </title>
                <description>
                    <![CDATA[ First, a confession: When I was learning to code, my “workflow” was a mess. Sticky notes. Google Docs. Random Trello boards I never checked again. And a to-do list that somehow never got any shorter. Then I joined a real team. Suddenly, I was introdu... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/a-beginner-developers-guide-to-kanban/</link>
                <guid isPermaLink="false">68815e6054ad71fa4b3b7bb6</guid>
                
                    <category>
                        <![CDATA[ agile ]]>
                    </category>
                
                    <category>
                        <![CDATA[ agile methodology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Agile Software Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Career ]]>
                    </category>
                
                    <category>
                        <![CDATA[ interview ]]>
                    </category>
                
                    <category>
                        <![CDATA[ kanban ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Kanban boards ]]>
                    </category>
                
                    <category>
                        <![CDATA[ project management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Product Management ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Product Design ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Developer ]]>
                    </category>
                
                    <category>
                        <![CDATA[ workflow ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Aditya Vikram Kashyap ]]>
                </dc:creator>
                <pubDate>Wed, 23 Jul 2025 22:12:48 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753300952223/508231c9-f0bc-4aa8-9c97-5ad4157891b9.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>First, a confession<strong>:</strong> When I was learning to code, my “workflow” was a mess. Sticky notes. Google Docs. Random Trello boards I never checked again. And a to-do list that somehow never got any shorter.</p>
<p>Then I joined a real team.</p>
<p>Suddenly, I was introduced to this thing called <strong>Kanban</strong> – and I realized I’d been treating software like a solo art project, not a process.</p>
<p>If that sounds familiar, you’re in the right place.</p>
<p>This guide will walk you through <strong>how Kanban actually works</strong>, how developers use it to track and prioritize work, and how it can help you stay sane when juggling bugs, features, and real-world deadlines.</p>
<p>Without further delay, lets get into it.</p>
<h3 id="heading-heres-what-well-cover">Here’s what we’ll cover:</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-so-what-is-kanban">So… What Is Kanban?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-classic-kanban-board-three-simple-columns">The Classic Kanban Board: Three Simple Columns</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-developers-use-kanban-in-real-life">How Developers Use Kanban in Real Life</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-kanban-vs-scrum-whats-the-difference">Kanban vs Scrum: What’s the Difference?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-so-which-one-should-you-use-scrum-or-kanban">So which one should you use Scrum or Kanban?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-tools-do-teams-use-for-kanban">What Tools Do Teams Use for Kanban?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-kanban-to-manage-your-own-coding-projects">How to Use Kanban to Manage Your Own Coding Projects</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts-why-kanban-isnt-just-a-board">Final Thoughts: Why Kanban Isn’t Just a Board</a></p>
</li>
</ul>
<h2 id="heading-so-what-is-kanban">So… What Is Kanban?</h2>
<p>At its core, Kanban is a <strong>visual way to manage work</strong>. It helps teams (or team members) see:</p>
<ul>
<li><p>What needs to get done</p>
</li>
<li><p>What’s in progress</p>
</li>
<li><p>What’s finished</p>
</li>
<li><p>Where things are getting stuck</p>
</li>
</ul>
<p>The concept comes from lean manufacturing, but in tech, it’s often used in Agile teams that need flexibility without the structure of Scrum sprints.</p>
<p>Think of Kanban like a whiteboard that tells a story. Not just what’s done, but how work flows.</p>
<h2 id="heading-the-classic-kanban-board-three-simple-columns">The Classic Kanban Board: Three Simple Columns</h2>
<p>So what exactly is a Kanban board? At its core, it’s a visual representation of your workflow – a board that shows all the work your team (or you, solo warrior) are juggling, and where each task stands.</p>
<p>It can be physical, like an actual whiteboard with sticky notes that move from one column to the next. Or digital, using tools like Trello, Jira, GitHub Projects, or Notion. The key is that it’s visual and up-to-date. You can walk into a room or open a tab and instantly understand: What’s being worked on? What’s ready to go? Where are things stuck?</p>
<p>It’s like having your brain on a wall, but organized. And slightly less chaotic.</p>
<p>The beauty of Kanban is how dead simple it is to get started. At minimum, your board has three columns:</p>
<table><tbody><tr><td><p><strong>&nbsp;To Do</strong></p></td><td><p><strong>In Progress</strong></p></td><td><p><strong>Done</strong></p></td></tr></tbody></table>

<p>Each task – or <strong>card</strong> – moves from left to right as it gets worked on.</p>
<p>Let’s say your team is building a blog platform. Your Kanban board might have cards like:</p>
<ul>
<li><p>“Create signup form”</p>
</li>
<li><p>“Fix image upload bug”</p>
</li>
<li><p>“Deploy staging build”</p>
</li>
</ul>
<p>Now, while Kanban is flexible, it can absolutely be taken too far.</p>
<p>I’ve seen boards with more columns than a Greek temple: “Needs Review,” “Pending Client Feedback,” “QA Rework Round 2,” “Blocked but Still Hopeful,” “In Existential Limbo,” and so on. Every card had six tags, three owners, two checklists, and one migraine.</p>
<p>The lesson? Don’t turn your board into a bureaucratic jungle.</p>
<p>You don’t need to account for every edge case. Start simple: “To Do,” “In Progress,” “Review,” “Done.” These basic stages cover most workflows. If you discover a real need for something more – like a dedicated “QA” column or “Blocked” column – add it intentionally, not because you feel like your board needs to look fancy.</p>
<p>Remember: A Kanban board should be helpful, not overwhelming. If you spend more time managing the board than doing the work on it… it’s doing the opposite of what it’s meant to do.</p>
<h2 id="heading-how-developers-use-kanban-in-real-life">How Developers Use Kanban in Real Life</h2>
<p>Here’s how you might interact with a Kanban board on a dev team:</p>
<ol>
<li><p>You pick up a card from “To Do” – let’s say, “Add dark mode toggle.”</p>
</li>
<li><p>You move it to “In Progress.”</p>
</li>
<li><p>When it’s ready for review, you might move it to a temporary “Review” or “Testing” column.</p>
</li>
<li><p>Once it’s merged, tested, and deployed, you move it to “Done.”</p>
</li>
<li><p>You smile, drink some coffee, and grab the next card.</p>
</li>
</ol>
<p>That’s it. But over time, this process helps the whole team:</p>
<ul>
<li><p>Spot bottlenecks</p>
</li>
<li><p>Prevent duplicate work</p>
</li>
<li><p>Reduce context switching</p>
</li>
<li><p>Keep everyone aligned</p>
</li>
</ul>
<h3 id="heading-whats-a-wip-limit-and-why-should-you-care">What’s a WIP Limit — And Why Should You Care?</h3>
<p>WIP = <strong>Work In Progress</strong>. This is the most important concept to keep us in check.</p>
<p>One of Kanban’s key principles is <strong>limiting how many things you’re working on at once</strong>. Because guess what? Multitasking kills momentum.</p>
<p>A typical WIP limit might look like:</p>
<ul>
<li><p>No more than 2–3 cards per person in “In Progress” Again this is best practice, but folks do pick up a lot and then they end up being the bottleneck.</p>
</li>
<li><p>No more than 5 tasks waiting on QA.</p>
</li>
</ul>
<p>Why? Because when everything’s urgent, nothing gets done. WIP limits force you to finish one thing before you start more – and that’s how real velocity happens.</p>
<p>If there are more than 5 tasks in the “To Do” column, the team doesn’t take up new ones. Instead, everyone chips in to see how they can help unclog the bottleneck. A bottleneck is your worst enemy in Kanban, and you want to resolve it so items move smoothly on time and on target.</p>
<p><a target="_blank" href="https://youtu.be/R8dYLbJiTUE?si=Hh00XXI4_1urv4Mp">Here’s a video</a> recapping key concepts.</p>
<h2 id="heading-kanban-vs-scrum-whats-the-difference"><strong>Kanban vs Scrum: What’s the Difference?</strong></h2>
<p>You’ve probably heard Scrum and Kanban mentioned in the same breath – and both are popular Agile frameworks. But they’re not interchangeable.</p>
<p>Scrum is structured, with roles like Product Owner and Scrum Master, and work gets organized into time-boxed sprints. It’s perfect for teams that benefit from rhythm and rituals – like sprint planning, daily standups, and retrospectives.</p>
<p>Kanban, on the other hand, is a little looser. No official roles, no set sprint timelines. Work flows continuously, and change can happen anytime. It’s perfect for teams who need more flexibility and fewer ceremonies.</p>
<p>So how do they compare in practice? Let’s break it down:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Key Differentiating Factors</strong></td><td><strong>Scrum</strong></td><td><strong>Kanban</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Time-based</td><td>Yes – 1–2 week sprints</td><td>No – continuous flow</td></tr>
<tr>
<td>Roles</td><td>PO, SM, Developers</td><td>No specific roles required</td></tr>
<tr>
<td>Planning</td><td>Sprint planning, retros, and so on</td><td>On-demand, just-in-time</td></tr>
<tr>
<td>Cadence</td><td>Fixed sprint cycle</td><td>Flexible, ongoing</td></tr>
<tr>
<td>Use case</td><td>Complex, structured teams</td><td>Continuous delivery teams</td></tr>
</tbody>
</table>
</div><p><strong>Bottom line:</strong></p>
<ul>
<li><p>Scrum is a scheduled loop. Kanban is a living flow.</p>
</li>
<li><p>One’s a playbook. The other’s a status window.</p>
</li>
</ul>
<p><a target="_blank" href="https://youtu.be/F5QIqFEDv2k?si=jvNoAiHmrv_iq-Lx">Here’s a video</a> on the main differences between Scrum and Kanban you can watch if you want more detail.</p>
<h2 id="heading-so-which-one-should-you-use-scrum-or-kanban"><strong>So which one should you use Scrum or Kanban?</strong></h2>
<p>So… which one should you use?</p>
<p>It really depends on your team, your product, and your pain points.</p>
<p>✔️ If you’re working on a brand-new product where requirements shift a lot, and your team thrives with structure and routines – Scrum is likely the better fit. Sprints give you a sense of pacing, and ceremonies help ensure alignment.</p>
<p>✔️ If you’re managing ongoing work like bug triage, tech debt, infrastructure tasks, or anything that’s more “whenever it comes in” than “we need to ship this in two weeks” – Kanban gives you flexibility and visibility without the overhead.</p>
<p>And yes, there’s such a thing as <strong>Scrumban</strong> – a hybrid approach where teams use visual boards and WIP limits from Kanban, but keep some of Scrum’s structure like standups and retros. It’s like Agile tapas: you get the flavors that work best for your appetite.</p>
<p><a target="_blank" href="https://youtu.be/kiI3IweyAeQ?si=M1mtS5HCCcGcT78J">Here is a detailed video</a> that’'ll teach you more about how Scrumban works in practice.</p>
<p>Watch the Scrumban video only when you are familiar and comfortable with both Scrum and Kanban – otherwise, you might get confused from the cross-pollination of ideas and frameworks.</p>
<p>I personally have never seen a Scrumban implementation thats scaled well – too many folks trying too many things and none of them work. But thats just based on my experience – it may work for you and your team. I’ll let you be the judge.</p>
<h2 id="heading-what-tools-do-teams-use-for-kanban"><strong>What Tools Do Teams Use for Kanban?</strong></h2>
<p>You’ve probably seen (or used) one already:</p>
<ul>
<li><p><strong>Trello</strong> – Simple and great for solo or small teams</p>
</li>
<li><p><strong>Jira</strong> – Enterprise-level, customizable workflows</p>
</li>
<li><p><strong>GitHub Projects</strong> – Lightweight but powerful for devs</p>
</li>
<li><p><strong>ClickUp / Asana / Notion</strong> – Integrated with docs/tasks</p>
</li>
</ul>
<p>Kanban isn’t tied to any one tool – you can use an app, a browser tab, or a whiteboard and a pack of sticky notes from the office supply closet. What matters is how you use it. But let’s walk through some of the most common tools and what they offer in a Kanban context:</p>
<h3 id="heading-trello">🟩 <strong>Trello</strong></h3>
<p>Trello is probably the easiest way to start with Kanban. It gives you a simple digital board with columns and cards you can drag and drop. It’s great for devs or small teams who don’t need tons of automation – just a clean place to track work visually.</p>
<h3 id="heading-jira">🟨 <strong>Jira</strong></h3>
<p>Jira is a heavyweight – and while it’s built for Scrum, it also supports robust Kanban boards. You can define custom workflows, use built-in reports like cumulative flow diagrams, enforce WIP limits, and manage team velocity. Ideal for large teams that need traceability, integrations, and permissions.</p>
<h3 id="heading-github-projects">🟦 <strong>GitHub Projects</strong></h3>
<p>If your code lives in GitHub, GitHub Projects is a clean way to stay close to your codebase. It lets you create Kanban-style boards with issues and pull requests as cards, so you’re never toggling between tools just to track what’s in progress.</p>
<h3 id="heading-clickup-asana-notion">🟧 <strong>ClickUp / Asana / Notion</strong></h3>
<p>These are all-in-one productivity platforms. They combine Kanban boards with documentation, team chat, calendars, and reporting. If your team needs more than just “move card left to right,” these tools let you manage projects, meetings, notes, and workflows in one place.</p>
<h3 id="heading-whiteboard-sticky-notes">🟪 <strong>Whiteboard + Sticky Notes</strong></h3>
<p>Don’t underestimate the analog approach. It’s fast. It’s visible. It’s tactile. Physically moving a task from “Doing” to “Done” gives you a sense of progress no digital tool can match. And when something’s blocked? Slap a red sticky on it and call it a day.</p>
<p>Bottom line: The best tool is the one your team will <em>actually</em> use. Fancy doesn’t beat consistent. And the actual tool doesn’t matter as much as the <strong>discipline</strong> your team has to actually use it.</p>
<h2 id="heading-how-to-use-kanban-to-manage-your-own-coding-projects"><strong>How to Use Kanban to Manage Your Own Coding Projects</strong></h2>
<p>Even if you're not on a team yet, Kanban is great for your own workflow. Here’s how you can use it to help yourself out:</p>
<ol>
<li><p>Create a basic 3-column board (To Do, In Progress, Done)</p>
</li>
<li><p>Write out every task, big or small</p>
</li>
<li><p>Set a WIP limit (for example, no more than 2 tasks at once)</p>
</li>
<li><p>Update it daily. Make it a ritual.</p>
</li>
<li><p>Review your flow weekly – What got stuck? What moved fast?</p>
</li>
</ol>
<p> Example:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>To-Do</strong></td><td><strong>In Progress</strong></td><td><strong>Done</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Fix CSS Layout</td><td>Add blog search bar</td><td>Set up Netlify</td></tr>
<tr>
<td>Write README</td><td></td><td>Deploy v1</td></tr>
</tbody>
</table>
</div><p>You’ll be shocked how much clearer your thinking gets when you can <em>see</em> your work. It’s simple but super powerful to visualize your work it in this way.</p>
<h2 id="heading-final-thoughts-why-kanban-isnt-just-a-board"><strong>Final Thoughts: Why Kanban Isn’t Just a Board</strong></h2>
<p>Kanban isn’t just a tool – it’s a mindset.</p>
<p>It helps you focus. It helps your team collaborate. And it gives everyone – even non-technical folks – visibility into what’s going on.</p>
<p>If you’re learning to code and want to feel more confident working with others, <strong>learning Kanban is low-effort, high-impact</strong>.</p>
<p>So don’t wait until your first job. Start using it now – and show up to that standup with confidence.</p>
<p>I hope this small 101 Guide to Kanban was helpful to you all. My sole purpose to write this was to help beginner developers understand Kanban as a practical workflow system – especially for those transitioning from solo coding to collaborative, real-world development environments. It aims to demystify the methodology in a casual, beginner-friendly tone while still offering actionable guidance.</p>
<p>I hope you enjoyed my beginners guide to Kanban.</p>
<p>Until next time, keep Learning, Unlearning and Relearning, folks….</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Constructors in Java: A Beginner's Guide ]]>
                </title>
                <description>
                    <![CDATA[ Java is an object-oriented programming language that is centred around the concept of objects. Objects are like real-world entities that are created with the new keyword and occupy memory. But all this happens in the front-end code – so what about th... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-constructors-in-java-a-beginners-guide/</link>
                <guid isPermaLink="false">686d6044aa83d1a2c46d6160</guid>
                
                    <category>
                        <![CDATA[ Java ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ constructors ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ateev Duggal ]]>
                </dc:creator>
                <pubDate>Tue, 08 Jul 2025 18:15:32 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1751998519087/7808c004-c8e5-4e63-b293-10fa479a179f.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Java is an object-oriented programming language that is centred around the concept of objects. Objects are like real-world entities that are created with the new keyword and occupy memory. But all this happens in the front-end code – so what about the back-end? How are objects created and initialised with values?</p>
<p>This is where constructors come into play. Constructors are special types of methods with no return type. They are basically used to initialise the object, to set up its internal state, or to assign default values to its attributes.</p>
<p>In this tutorial, we will go deep into the topic of constructors in Java. You’ll learn how they work and why they are essential in object creation and Java programming. By the end, I hope you’ll understand why they’re one of the core concepts of OOP.</p>
<p>Let’s start…</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>You don’t need to know anything too advanced to start learning about constructors in Java. Just a basic understanding of Java syntax, classes, objects, methods, parameters, arguments, and access modifiers is enough to get started.</p>
<h2 id="heading-what-well-cover">What we’ll cover:</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-are-constructors-in-java">What are Constructors in Java?</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-constructor-syntax">Constructor syntax:</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-types-of-constructors">Types of Constructors</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-default-constructor">Default Constructor</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-no-argument-constructor">No Argument constructor</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-parameterised-constructor">Parameterised Constructor</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-copy-constructor">Copy Constructor</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-what-happens-behind-the-scenes-when-a-constructor-is-called-in-java">What Happens Behind the Scenes When a Constructor Is Called in Java?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-the-return-keyword-in-constructors">How to Use the return Keyword in Constructors</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-sample-code">Sample Code:</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-example-4">Example:</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-frequently-asked-questions">Frequently Asked Questions</a></p>
</li>
</ul>
<h2 id="heading-what-are-constructors-in-java"><strong>What are Constructors in Java?</strong></h2>
<p>As mentioned above, constructors are special types of methods that:</p>
<ol>
<li><p>do not have a return type (not even void), </p>
</li>
<li><p>have the same name as the class</p>
</li>
<li><p>are called automatically when an object is created using the new keyword. </p>
</li>
</ol>
<p>A constructor’s main purpose is to initialise a newly created object, to set up its internal state, or to assign default values to its attributes.</p>
<p>Constructors can also be understood as a special block of code which is called when an object is created – either automatically or manually by hard-coding it – with the values we want to initialise the object with.</p>
<p>If we’re okay with the object using default values (like 0 for numbers or null for objects), Java will handle that for us automatically. But if we want to give the object specific values when it's created, we need to write a constructor that takes those values as parameters and uses them to set up the object.</p>
<h3 id="heading-constructor-syntax"><strong>Constructor syntax:</strong></h3>
<pre><code class="lang-java"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ClassName</span> </span>{

    <span class="hljs-comment">// Default constructor with access modifier</span>
    [access_modifier] ClassName(parameters...) {
        <span class="hljs-comment">// constructor body</span>
    }

}
</code></pre>
<h3 id="heading-examples">Examples</h3>
<p><strong>When the constructor is not defined explicitly</strong></p>
<pre><code class="lang-java"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Car</span> </span>{

    String brand;
    <span class="hljs-keyword">int</span> year;

    <span class="hljs-comment">// No constructor is defined, so Java provides a default one</span>
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Main</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{

        Car car1 = <span class="hljs-keyword">new</span> Car();  <span class="hljs-comment">// Java calls the default constructor</span>

        <span class="hljs-comment">// Default values: brand = null, year = 0</span>
        System.out.println(<span class="hljs-string">"Brand: "</span> + car1.brand);
        System.out.println(<span class="hljs-string">"Year: "</span> + car1.year);
    }
}
</code></pre>
<p>Output:</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXcTW6FYYmq8kB1QL_vSBNqbaVBgo7hLXvqmA3l52HBh9Yvq4AN1aLIAKRqqiOz_tDcCFOTWBVoO1bgjWOD2yyt1nykuobAPQTWRayjqK0jDu2COmPxqI5AaapIyFzDbkrvreV-qyw?key=-LGNq3k7xufJJBHkVFXMZw" alt="output of the code in which there is no constructor" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>In the above code, we have a <strong>Car</strong> class with two variables:</p>
<ol>
<li><p>brand of type <code>String</code></p>
</li>
<li><p>year of type <code>int</code></p>
</li>
</ol>
<p>Since a class is just a blueprint, we need to create an object to actually use it. This is done in the <code>Main</code> class. When we create a Car object using <code>new</code> Car(), Java looks for a constructor. Because we didn’t define one, the compiler automatically provides a default constructor (one with no arguments).</p>
<p>This allows us to create the object and print its variables without any errors. The values printed will be the default ones — <code>null</code> <strong>for the</strong> <code>String</code><strong>, and</strong> <code>0</code> <strong>for the</strong> <code>int</code><strong>.</strong></p>
<p>We'll dive deeper into how this works behind the scenes later, step by step, so it becomes easier to understand.</p>
<p><strong>When we have defined a constructor</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Car</span> </span>{

    String brand;
    <span class="hljs-keyword">int</span> year;

    <span class="hljs-comment">// Constructor with parameters to initialize custom values</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Car</span><span class="hljs-params">(String brandName, <span class="hljs-keyword">int</span> modelYear)</span> </span>{
        brand = brandName;
        year = modelYear;
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Main</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{

        Car car2 = <span class="hljs-keyword">new</span> Car(<span class="hljs-string">"Toyota"</span>, <span class="hljs-number">2022</span>);  <span class="hljs-comment">// Custom values</span>

        System.out.println(<span class="hljs-string">"Brand: "</span> + car2.brand);
        System.out.println(<span class="hljs-string">"Year: "</span> + car2.year);
    }
}
</code></pre>
<p><strong>Output</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751864448284/2cbb8360-1c6d-42b8-811f-b7603625288d.png" alt="Output of the code which has a constructor." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>This is the same code as before, with one key difference: this time, we’ve explicitly defined a constructor. Because of this, the output we see isn’t the default values (<code>null</code> for <code>String</code>, <code>0</code> for <code>int</code>), but the custom values we provided.</p>
<p>How does that happen? Simple – we pass values as arguments when creating the object:</p>
<p><code>Car car2 = new Car("Toyota", 2022);</code></p>
<p>These values are received by the constructor as parameters and are then used to initialize the object’s variables. As a result, instead of default values, we get the brand and year we specified.</p>
<h2 id="heading-types-of-constructors"><strong>Types of Constructors</strong></h2>
<p>There are mainly four types of constructors:</p>
<ol>
<li><p>Default Constructors</p>
</li>
<li><p>No-Arguments Constructor</p>
</li>
<li><p>Parameterised Constructor</p>
</li>
<li><p>Copy Constructor</p>
</li>
</ol>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfafO5dDmX5UA0ADQI5Q8DZSU2H_bVlHjmtKdDpMkmWB4Rhui1kR4w_BP_7-mPz6eb9KdGkVmYxYsZHa4HI044mz3O0CXtXJZBhpJr_wCqWgLO6U0BDUNzm_C9piUHfyXr84xEsoQ?key=-LGNq3k7xufJJBHkVFXMZw" alt="Types of constructors" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-default-constructor"><strong>Default Constructor</strong></h3>
<p>A type of no-argument constructor that is added by the compiler during the compilation process so that the values of the object can be initialised. It’s only added by the compiler if you don’t add one explicitly.</p>
<h4 id="heading-syntax"><strong>Syntax:</strong></h4>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyClass</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">MyClass</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// Constructor body</span>
    }

}
</code></pre>
<h4 id="heading-example"><strong>Example</strong></h4>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bike</span> </span>{

    <span class="hljs-comment">// No constructor defined here</span>
    <span class="hljs-comment">// Compiler will automatically add a default constructor</span>

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        Bike myBike = <span class="hljs-keyword">new</span> Bike();  <span class="hljs-comment">// Calls the compiler-provided default constructor</span>
        System.out.println(<span class="hljs-string">"Bike object created!"</span>);
    }

}
</code></pre>
<p>The code becomes the following after the compiler adds a default constructor during the compilation process:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bike</span> </span>{

    <span class="hljs-comment">// Compiler-added default constructor</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Bike</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">super</span>();  <span class="hljs-comment">// Calls Object class constructor</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        Bike myBike = <span class="hljs-keyword">new</span> Bike();  <span class="hljs-comment">// Now calls this explicit default constructor</span>
        System.out.println(<span class="hljs-string">"Bike object created!"</span>);
    }

}
</code></pre>
<h4 id="heading-output"><strong>Output</strong></h4>
<p>Bike object created!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751867033814/3296a90c-3576-4a57-a863-fe0d1acfafa2.png" alt="Default Constructor" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-no-argument-constructor"><strong>No Argument constructor</strong></h3>
<p>No-argument constructor is a type of constructor that you explicitly write in your code and that does not contain any parameters.</p>
<p>Now, you may be wondering…Isn't it the same as the default constructor? The answer is both yes and no.</p>
<p>There isn’t much difference between the default constructor and the no-argument constructor, as both do not take any parameters. But there is one key difference.</p>
<p>The default constructor, as we have already discussed, is a type of no-argument constructor that is automatically added by the compiler when it doesn’t find one in our code. In contrast, a no-argument constructor is a type of constructor that we write in our code. </p>
<p>In short, if the compiler is the one that is adding a constructor during the compilation process, it's called a default constructor. But if we are the ones adding the constructor, it’s called a no-argument constructor. </p>
<p>The main difference between a default constructor and a user-defined constructor is <strong>how they are created and what they do</strong>.</p>
<ul>
<li><p>A <strong>default constructor</strong> is automatically added by the compiler <strong>if we don’t add one ourselves</strong>. It doesn’t do much – it just calls the parent class (usually the <code>Object class</code>) and sets all variables to their default values. For example, <code>int</code> becomes <code>0</code>and objects become <code>null</code>.</p>
</li>
<li><p>A <strong>user-defined constructor</strong> is one that <strong>we write ourselves</strong>. We can add custom logic inside it, set custom values to variables, and use access modifiers like <code>public</code>, <code>private</code>, or <code>protected</code>. This means we can decide how the object should be set up when it is created.</p>
</li>
</ul>
<p>Note that even if we don’t write <code>super()</code> in our constructor, Java still adds it automatically unless we call another constructor with <code>this()</code> or call a different <code>super(...)</code> with parameters.</p>
<p>We will understand this deeply in the next section.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Aspect</strong></td><td><strong>Default Constructor</strong></td><td><strong>No-Argument Constructor</strong></td></tr>
</thead>
<tbody>
<tr>
<td><strong>Definition</strong></td><td>A constructor is automatically provided by the compiler when no other constructors exist.</td><td>A constructor explicitly written by the programmer that takes no arguments.</td></tr>
<tr>
<td><strong>Defined By</strong></td><td>Compiler</td><td>Programmer</td></tr>
<tr>
<td><strong>Custom Logic</strong></td><td>Not possible – does only basic, default initialization</td><td>Yes – can contain any initialization logic</td></tr>
<tr>
<td><strong>When Available</strong></td><td>Only if the class has no constructors defined at all</td><td>When explicitly written by the programmer</td></tr>
<tr>
<td><strong>Purpose</strong></td><td>To allow object creation with default initialization</td><td>To allow object creation with programmer-defined behavior</td></tr>
</tbody>
</table>
</div><h4 id="heading-syntax-1"><strong>Syntax:</strong></h4>
<pre><code class="lang-java"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ClassName</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ClassName</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// Body (optional)</span>
    }

}
</code></pre>
<h4 id="heading-example-1"><strong>Example</strong></h4>
<p>Let's use the same Bike example we used to explain the default constructor.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bike</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Bike</span><span class="hljs-params">()</span> </span>{
        System.out.println(<span class="hljs-string">"Bike object created!"</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        Bike myBike = <span class="hljs-keyword">new</span> Bike();
    }
}
</code></pre>
<h4 id="heading-output-1"><strong>Output:</strong></h4>
<p>Bike object created!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751868234945/565ec06d-7e4d-4415-b983-f517c721d0b9.png" alt="No Argument Construment" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>In the above code, we have defined a constructor in our code while writing it. This means that it is an example of a no-argument constructor.</p>
<p>We know that both types of constructors are defined without any parameters, but what about the body? We haven’t said anything about it. Let’s see what happens if we write code with a no-argument constructor without a body:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bike</span> </span>{

    Bike() {
        <span class="hljs-comment">// No body</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        Bike myBike = <span class="hljs-keyword">new</span> Bike(); <span class="hljs-comment">// Calls the user-defined no-argument constructor</span>
        System.out.println(<span class="hljs-string">"Bike object created!"</span>);
    }
}
</code></pre>
<h4 id="heading-output-2"><strong>Output:</strong></h4>
<p>Bike object created!</p>
<p>The code still gets compiled because the compiler adds the <code>super()</code> keyword during the compilation process, which initialises the object using the object class.</p>
<h3 id="heading-parameterised-constructor"><strong>Parameterised Constructor</strong></h3>
<p>A constructor that accepts parameters is called a parameterised constructor and is only used when we have to initialise an object’s attributes with custom values. </p>
<ul>
<li><p>Parameter refers to the variable listed in the constructor or method definition.</p>
</li>
<li><p>Argument is the actual value passed when calling the constructor or method.</p>
</li>
</ul>
<p>It gives us the flexibility of initiating our object with custom values given at the time of object creation.</p>
<h4 id="heading-syntax-2"><strong>Syntax</strong></h4>
<p>Below is the syntax for a parameterised constructor that takes one parameter:</p>
<pre><code class="lang-java"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ClassName</span> </span>{

    <span class="hljs-comment">// Data members (instance variables)</span>
    DataType variable1;

    <span class="hljs-comment">// Parameterized constructor</span>
    ClassName(DataType param1) {
        variable1 = param1;
    }

    <span class="hljs-comment">// Main method to create objects</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-comment">// Creating object using parameterized constructor</span>
        ClassName obj = <span class="hljs-keyword">new</span> ClassName(value1);
    }
}
</code></pre>
<h4 id="heading-example-2"><strong>Example</strong></h4>
<p>We will again use the Bike example for this.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bike</span> </span>{

    String modelName;  <span class="hljs-comment">// instance variable</span>

    <span class="hljs-comment">// Parameterized constructor</span>
    Bike(String model) {
        modelName = model;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-comment">// Pass parameter while creating the Bike object</span>
        Bike myBike = <span class="hljs-keyword">new</span> Bike(<span class="hljs-string">"Mountain Bike"</span>);
        System.out.println(<span class="hljs-string">"Bike object created! Model: "</span> + myBike.modelName);
    }
}
</code></pre>
<h4 id="heading-output-3">Output:</h4>
<p>Bike object created! Model: Mountain Bike</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751870395133/72bcbe0c-3a7c-4cc1-87d4-1ae9106b21d9.png" alt="Parameterized Constructor" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>In this example, we’re working with a Bike class that has an instance variable of String data type called modelName, and a constructor to set the value of that variable.</p>
<p>The constructor takes a parameter called model and assigns it to modelName. So, when we create a new Bike object and pass in the string "Mountain Bike", the constructor stores that value in the modelName variable.</p>
<p>Because of this, when we print out the model name, we see "Mountain Bike" instead of null, which is the default value of the String data type, as now the value of the modelName has been updated.</p>
<h3 id="heading-copy-constructor"><strong>Copy Constructor</strong></h3>
<p>A copy constructor is used to create a new object as a copy of the existing object. Unlike C++, Java doesn’t have a default copy constructor. Instead, we have to create our own by creating a constructor that takes an object of the same class as a parameter and copies its fields.</p>
<h4 id="heading-syntax-3"><strong>Syntax</strong></h4>
<pre><code class="lang-java"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ClassName</span> </span>{

    <span class="hljs-comment">// Fields</span>
    DataType1 field1;
    DataType2 field2;
    <span class="hljs-comment">// ... other fields</span>

    <span class="hljs-comment">// Normal constructor</span>
    ClassName(DataType1 f1, DataType2 f2) {
        field1 = f1;
        field2 = f2;
        <span class="hljs-comment">// ... initialize other fields</span>
    }

    <span class="hljs-comment">// Copy constructor </span>
    ClassName(ClassName other) {
        field1 = other.field1;
        field2 = other.field2;
        <span class="hljs-comment">// ... copy other fields</span>
    }
}
</code></pre>
<h4 id="heading-example-3"><strong>Example</strong></h4>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bike</span> </span>{

    String modelName;  <span class="hljs-comment">// instance variable</span>

    <span class="hljs-comment">// Parameterized constructor</span>
    Bike(String model) {
        modelName = model;
    }

    <span class="hljs-comment">// Copy constructor</span>
    Bike(Bike otherBike) {
        modelName = otherBike.modelName;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-comment">// Create a Bike object using the parameterized constructor</span>
        Bike myBike = <span class="hljs-keyword">new</span> Bike(<span class="hljs-string">"Mountain Bike"</span>);
        System.out.println(<span class="hljs-string">"Bike object created! Model: "</span> + myBike.modelName);

        <span class="hljs-comment">// Create a copy of the existing Bike object using the copy constructor</span>
        Bike copiedBike = <span class="hljs-keyword">new</span> Bike(myBike);
        System.out.println(<span class="hljs-string">"Copied Bike object created! Model: "</span> + copiedBike.modelName);
    }
}
</code></pre>
<h4 id="heading-output-4"><strong>Output:</strong></h4>
<p>Bike object created! Model: Mountain Bike</p>
<p>Copied Bike object created! Model: Mountain Bike</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751874707838/772bdc8a-75d7-437d-a296-7f472fe5c764.png" alt="Copy Constructor" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>In the above code, we have created a copy constructor to copy the values of the object (myBike) into a new object (copiedBike), which we have defined in the main class.</p>
<p>But the way the new object is called is a little different. Instead of passing arguments for the constructor, we have passed the original object.</p>
<h4 id="heading-why-copy-constructors"><strong>Why Copy Constructors?</strong></h4>
<p>A copy constructor is used to make a copy of the object, but you can also make a copy using the clone() method or the object.clone() method. So why do we use a copy constructor?</p>
<p>The copy constructor makes a deep copy, while the clone method makes a shallow copy of the object. There are various things that you should know before using cloning techniques, like <a target="_blank" href="https://docs.oracle.com/javase/8/docs/api/java/lang/CloneNotSupportedException.html">CloneNotSupportedException</a><strong>.</strong></p>
<p>On the other hand, copy constructors are clear and easy to understand, and work well with final fields. We can control how the copy happens (deep vs. shallow) and especially when we are dealing with mutable objects.</p>
<h2 id="heading-what-happens-behind-the-scenes-when-a-constructor-is-called-in-java"><strong>What Happens Behind the Scenes When a Constructor Is Called in Java?</strong></h2>
<p>So, just to recap: when we create an object using the <code>new</code> keyword, a constructor is automatically called. If we haven't defined any constructors in our class, Java automatically defines a constructor for us. </p>
<p>But while writing and running our code, we mostly focus on what’s visible in our editor, as in what we can see. Let’s dive a little deeper and explore what happens behind the scenes – at the compiler and JVM level – when an object is created and executed.</p>
<ul>
<li><p><strong>Step 1: Memory Allocation</strong> – When we create an object using a new keyword, Java allocates memory for that object in the heap. This memory is where the object’s fields (also called attributes) will be placed.</p>
</li>
<li><p><strong>Step 2: Reference Creation</strong> – A reference to this object is stored on the stack, which lets our program interact with the object that lives in the heap.</p>
</li>
<li><p><strong>Step 3: Constructor Creation</strong> – Java then determines which constructor to call. If no constructor is explicitly defined in our class, the compiler automatically inserts a constructor with no parameters.</p>
</li>
<li><p><strong>Step 4: Superclass Constructor Call</strong> – Before executing the constructor’s body, Java first calls the constructor of the superclass using the <code>super()</code> keyword. This ensures that the fields inherited from the parent class are properly initialised. If you don’t explicitly write <code>super()</code>, the compiler adds it automatically in the first line of the code, but only if the superclass has a no-argument constructor, unless we're already calling another constructor via <code>this()</code>.</p>
</li>
</ul>
<p>But don’t use both the <code>super()</code> and <code>this()</code> keywords in the same constructor (you can use them in separate constructors.</p>
<p>Let’s say that it doesn’t have a super class – then what? </p>
<p>The answer is simple: Java has an in-built Object class that has a no-argument constructor by default.  This is why our classes run smoothly even if we don’t write super() ourselves, as Java calls it in the background. </p>
<p>That means every class we create is a subclass of the object class:</p>
<h4 id="heading-attribute-initialisation">Attribute Initialisation:</h4>
<p>At this point, fields get initialised:</p>
<ul>
<li><p>First, with default values (for example, 0 for int, null for objects),</p>
</li>
<li><p>Then, with any explicit initialisations we've written (for example, int x = 10), the default values will get replaced by them.</p>
</li>
</ul>
<h4 id="heading-constructor-execution">Constructor Execution:</h4>
<p>And finally, the logic runs. This is where all or some of the attributes defined in the class for object creation are initialised by the parameters used during object creation, with the help of constructors. </p>
<p>But not every field may get initialised. Fields that are not updated by the constructor will keep the values they already have (either the default value or the explicitly initialised values). </p>
<p>In short, the constructor gives us the flexibility to customise our object at the time of creation, but it doesn't automatically set every field unless we explicitly write the logic for it.</p>
<p>Check the code below to understand better:</p>
<pre><code class="lang-java"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Example</span> </span>{

    <span class="hljs-keyword">int</span> a;           <span class="hljs-comment">// default 0</span>
    <span class="hljs-keyword">int</span> b = <span class="hljs-number">10</span>;      <span class="hljs-comment">// explicitly initialized to 10</span>
    String name;     <span class="hljs-comment">// default null</span>

    Example(<span class="hljs-keyword">int</span> x) {
        a = x;       <span class="hljs-comment">// only 'a' is set through constructor</span>
        <span class="hljs-comment">// 'b' is not changed, stays 10</span>
        <span class="hljs-comment">// 'name' is not changed, stays null</span>
    }

    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">display</span><span class="hljs-params">()</span> </span>{
        System.out.println(<span class="hljs-string">"a = "</span> + a);
        System.out.println(<span class="hljs-string">"b = "</span> + b);
        System.out.println(<span class="hljs-string">"name = "</span> + name);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        Example obj = <span class="hljs-keyword">new</span> Example(<span class="hljs-number">5</span>);
        obj.display();
    }
}
</code></pre>
<p><strong>Output:</strong></p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfRr6j8-QeGffPm7DYMImJL5s9X-apEGhLzXAv_cNw2CcwONejKxd4-_xKbmdKGSW1w09lt3Pib_psv7RLkd5LJ1uUxd9LSU2KOcDU9kYeqjYbZWS-qUN1PuLbNV8uF0M373kl7Cw?key=-LGNq3k7xufJJBHkVFXMZw" alt="Output of the code explaining how constructor work." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>In the above example, we have three data members – a, b, and name. We have already done the declaration and initialisation of the variable b at the beginning and given a value to a at the time of object creation.</p>
<p>So we can see that:</p>
<ol>
<li><p>‘<strong>a</strong>’, whose value has been updated by the constructor with the value given at the time of object creation, has the same value</p>
</li>
<li><p><strong>‘b’,</strong> which already had a value and does not get updated by the constructor, prints the same value </p>
</li>
<li><p>the string <strong>‘name’</strong> didn’t have a value, so null was printed instead, as it is the default value of the String data type.</p>
</li>
</ol>
<h2 id="heading-how-to-use-the-return-keyword-in-constructors"><strong>How to Use the</strong> <code>return</code> <strong>Keyword in Constructors</strong></h2>
<p>We know that constructors are defined without a return type, but we can use the return keyword in the constructor only to exit the constructor early, not to return a value. Check out the code below.</p>
<h3 id="heading-sample-code"><strong>Sample Code:</strong></h3>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bike</span> </span>{

    String modelName;  <span class="hljs-comment">// instance variable</span>
    <span class="hljs-keyword">int</span> speed;

    <span class="hljs-comment">// Parameterized constructor</span>
    Bike(String model, <span class="hljs-keyword">int</span> sp) {
        modelName = model;
        <span class="hljs-keyword">return</span>;
        speed = sp;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-comment">// Pass parameter while creating the Bike object</span>
        Bike myBike = <span class="hljs-keyword">new</span> Bike(<span class="hljs-string">"Mountain Bike"</span>, <span class="hljs-number">20</span>);
        System.out.println(<span class="hljs-string">"Bike object created! Model: "</span> + myBike.modelName);
        System.out.println(<span class="hljs-string">"Speed of the Bike is "</span> + myBike.speed);
    }
}
</code></pre>
<p>Let’s try to understand the above code and the use of the return keyword along with it. We will start with what would happen if the return keyword wasn’t here. The code would have executed without any errors and would have received an output.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXdRpxqN-7mjbNNenChHDcIxtufG5P4LQOG1GXo4L7_kFz955k-YF2HJfy96ZoIbPtxy3flUKiw4Mq6C8qSdZEnw3bzg5rbAy3BR4Q4x7uO2EjZfN7zFGDRlCWbAth_s97TGvoHpCQ?key=-LGNq3k7xufJJBHkVFXMZw" alt="code without the return keyword" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Now, what will happen if we add the return keyword? As we have discussed above, the return keyword will tell the compiler not to go beyond this point in the constructor. </p>
<p>So whatever we have written in the constructor after the return keyword will not be compiled, and if that had any value and was necessary for the proper execution of our code, the compiler will throw an error.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXc63VgyWkEdvcPr8x9TkVb-ZJKuPO2Z-ips_sNT70zGwFYRRdzfYYWG1Bfwnjmdypz-Pt8YS6SwQGBKMNrNhz4J27psGuOxz9Fs0gLVpM0oHzn5N1J0w3wgL7HU7rUQlB200qoRoA?key=-LGNq3k7xufJJBHkVFXMZw" alt="code with the return keyword" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>The error says it clearly ‘unreachable statement’, which means that the compiler was not allowed to go beyond the return keyword. </p>
<p>Now that you understand the return keyword, let’s see when you can use it.</p>
<h3 id="heading-example-4"><strong>Example:</strong></h3>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bike</span> </span>{

    <span class="hljs-comment">// Constructor with a condition to exit early</span>
    Bike(<span class="hljs-keyword">boolean</span> skip) {
        <span class="hljs-keyword">if</span> (skip) {
            System.out.println(<span class="hljs-string">"Constructor exited early"</span>);
            <span class="hljs-keyword">return</span>; <span class="hljs-comment">// Ends constructor execution here</span>
        }

        System.out.println(<span class="hljs-string">"Constructor continues..."</span>);
        <span class="hljs-comment">// More initialization logic can go here</span>
        System.out.println(<span class="hljs-string">"Bike object initialized successfully"</span>);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        System.out.println(<span class="hljs-string">"Creating first bike (skip = true):"</span>);
        Bike bike1 = <span class="hljs-keyword">new</span> Bike(<span class="hljs-keyword">true</span>);  <span class="hljs-comment">// Constructor will exit early</span>

        System.out.println(<span class="hljs-string">"\nCreating second bike (skip = false):"</span>);
        Bike bike2 = <span class="hljs-keyword">new</span> Bike(<span class="hljs-keyword">false</span>); <span class="hljs-comment">// Constructor will continue</span>
    }
}
</code></pre>
<p>We’ve defined a Bike class that has a constructor with one boolean parameter called skip. Inside the constructor, there's an if statement that checks if skip is true. If it is, the constructor prints a message and uses the return keyword to exit early. This means the rest of the constructor won’t run.</p>
<p>But there is no else block. So what happens when skip is false? In that case, the if condition is not true, then the code inside the if statement is not executed (including the return keyword) and the constructor simply continues to the next lines of code. That’s where we do the actual bike initialisation and print a success message.</p>
<p>In short:</p>
<ol>
<li><p>If skip is true, the constructor exits early.</p>
</li>
<li><p>If skip is false, the constructor continues and finishes the setup.</p>
</li>
</ol>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXeK-1kbbG97wMT_d-zrZ28eCuVsWZEkH6Ve3kuYSyXM1e-pZDsJl8K1S4GVfp54XnxGLOGGH_y_B3lZvmy1GRSvOY5Xp_rqHZd7jdNHHxdVAdVuW5vM__6DU99SdS38b03jXjkj?key=-LGNq3k7xufJJBHkVFXMZw" alt="output of the example explaining return keyword" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>This is a simple way to control how much of the constructor runs, based on a condition.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>In this blog, we have understood many different topics, what a constructor is, its different types, like default constructor, no argument constructor, parameterised constructor and copy constructor, not only with theory but with code examples as well.</p>
<p>Understanding them will not only enhance our understanding but also help us write modular and well-maintained code in Java. While this concept is also important in OOP, as it is centred around the concept of objects and constructors are the ones that are used to initialise them.</p>
<h2 id="heading-frequently-asked-questions"><strong>Frequently Asked Questions</strong></h2>
<p><strong>Q1. Why do we use constructors?</strong></p>
<p><strong>A:</strong> We use constructors because:</p>
<ol>
<li><p>They are created automatically by the compiler and initialise the object with default values.</p>
</li>
<li><p>We can initialise all the attributes of the objects in one go.</p>
</li>
<li><p>They prevent incomplete or incorrect object initialisation by ensuring that important data is provided during object creation.</p>
</li>
<li><p>Code maintainability and modularity increase.</p>
</li>
<li><p>We can use objects as soon as they are created.</p>
</li>
</ol>
<p><strong>Q2. What is the basic difference between Method Overloading and Constructor Overloading?</strong></p>
<p><strong>A:</strong> </p>
<table><tbody><tr><td><p><strong>Feature</strong></p></td><td><p><strong>Method Overloading</strong></p></td><td><p><strong>Constructor Overloading</strong></p></td></tr><tr><td><p><strong>Purpose</strong></p></td><td><p>To perform different operations with the same method name</p></td><td><p>To create objects with different initialisations</p></td></tr><tr><td><p><strong>Return Type</strong></p></td><td><p>Can have a return type</p></td><td><p>Has no return type</p></td></tr><tr><td><p><strong>Name</strong></p></td><td><p>Can be any valid method name</p></td><td><p>Always has the same name as the class</p></td></tr><tr><td><p><strong>Usage Context</strong></p></td><td><p>Called on existing objects</p></td><td><p>Called when creating objects</p></td></tr></tbody></table>

<p>You can check out some of my other beginner-friendly articles on my blog:</p>
<ol>
<li><p><a target="_blank" href="https://tekolio.com/what-is-abstraction-in-java-and-how-to-achieve-it/">Understanding abstraction in Java</a></p>
</li>
<li><p><a target="_blank" href="https://tekolio.com/how-to-build-a-movie-app-in-react-using-tmdb-api/">How to build a Movie App in React using TMDB API?</a></p>
</li>
<li><p><a target="_blank" href="https://tekolio.com/how-to-merge-two-sorted-arrays/">How to merge two sorted arrays</a></p>
</li>
</ol>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ An Animated Introduction to Programming with Python ]]>
                </title>
                <description>
                    <![CDATA[ Python is a high-level, general-purpose programming language. Created by Guido van Rossum and first released in 1991, Python is known for its design philosophy that emphasizes code readability, notably using significant indentation. It supports multi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/an-animated-introduction-to-programming-with-python/</link>
                <guid isPermaLink="false">685052f79796239b8044baeb</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mark Mahoney ]]>
                </dc:creator>
                <pubDate>Mon, 16 Jun 2025 17:23:03 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750082209046/ed07548b-e859-47a0-8372-6e9b04cb51bc.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><a target="_blank" href="https://www.python.org/downloads/">Python</a> is a high-level, general-purpose programming language. Created by <a target="_blank" href="https://en.wikipedia.org/wiki/Guido_van_Rossum">Guido van Rossum</a> and first released in 1991, Python is known for its design philosophy that emphasizes code readability, notably using significant indentation. It supports multiple programming paradigms, including object-oriented, imperative, and functional programming.</p>
<p>Python's popularity stems from its versatility and ease of use, making it an excellent choice for a broad range of applications. Its extensive standard library provides tools for numerous tasks, from web development and data analysis to artificial intelligence and scientific computing.</p>
<p>The language's clear syntax and active, supportive community also contribute to its widespread adoption among both beginners and experienced developers.</p>
<p>Python is employed across a wide array of domains, including:</p>
<ul>
<li><p>Data Analysis and Visualization: Processing, analyzing, and visualizing large datasets using libraries such as Pandas, NumPy, and Matplotlib.</p>
</li>
<li><p>Artificial Intelligence and Machine Learning: Developing AI models, machine learning algorithms, and deep learning applications with frameworks like TensorFlow and PyTorch.</p>
</li>
<li><p>Scientific and Numeric Computing: Performing complex calculations and simulations in fields like physics, engineering, and mathematics.</p>
</li>
<li><p>Web Development: Building server-side web applications with frameworks like Django and Flask.</p>
</li>
<li><p>Automation and Scripting: Automating repetitive tasks, system administration, and network configuration.</p>
</li>
<li><p>Software Testing and Quality Assurance: Writing scripts for automated testing.</p>
</li>
<li><p>Education: Widely used as a first language for teaching programming concepts due to its simplicity and readability.</p>
</li>
</ul>
<h2 id="heading-an-animated-introduction-to-programming-with-python"><strong>An Animated Introduction to Programming with Python</strong></h2>
<p>To make learning about Python programming more accessible, I developed an interactive tutorial called "An Animated Introduction to Programming with Python." This resource utilizes annotated code playbacks to demonstrate key language features step-by-step. From fundamental syntax to media manipulation, each concept is presented through executable code and accompanying visual explanations.</p>
<p>You can access the free 'book' of code playbacks here: <a target="_blank" href="https://playbackpress.com/books/pybook">https://playbackpress.com/books/pybook</a>.</p>
<p>For more information about code playbacks, you can watch a short demo.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/uYbHqCNjVDM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-a-focus-on-media-computation-with-jes"><strong>A Focus on Media Computation with JES</strong></h2>
<p>A portion of this book utilizes Mark Guzdial's pioneering "Media Computation" approach, which teaches programming concepts through the manipulation of digital media (images, sounds, and videos). Some of the examples in the book use the <a target="_blank" href="https://github.com/gatech-csl/jes">Jython Environment for Students</a> (JES).</p>
<h3 id="heading-a-note-on-jes"><strong>A Note on JES:</strong></h3>
<p>JES was developed at Georgia Tech and has been a highly effective pedagogical tool for many years. The main idea is to manipulate pixels in images for understanding fundamental programming concepts like iteration, conditionals, and functions in a visual and tangible way.</p>
<p>Even if JES isn't as widely used today, the lessons learned from manipulating pixels transfers to almost all other areas of computing. In other words, even if you don’t use JES it is still worth going through the playbacks marked with <em>JES.</em>**</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ol>
<li><p>Flow of Control and Simple Data</p>
<ol>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/1/1">Printing and flow</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/1/2">Arithmetic and comparing numbers</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/1/3">Programming with Data</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/1/4">Distance Between Two Points</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/1/5">More with Strings</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/1/6"><strong>JES:</strong> Prompting the User for Some Information</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/1/7"><strong>JES:</strong> Showing a Picture</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/1/8"><strong>JES:</strong> Accessing Pixels</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/1/9"><strong>JES:</strong> Adding a Caption to a Picture</a></p>
</li>
</ol>
</li>
<li><p>Iterating Over Data</p>
<ol>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/2/1">Iterating Through a String</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/2/2">Lists and Iteration</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/2/3">Splitting Strings</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/2/4">Ranges</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/2/5">Reading from a File</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/2/6">Writing to a File</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/2/7"><strong>JES:</strong> Iterating Through Pixels</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/2/8"><strong>JES:</strong> Graying an Image</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/2/9"><strong>JES:</strong> Copying an Image</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/2/10"><strong>JES:</strong> Enlarging a Picture</a></p>
</li>
</ol>
</li>
<li><p>Conditions with if and while</p>
<ol>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/3/1">Comparisons by the Computer</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/3/2">if, if/else, and if/else if/else Statements</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/3/3">Logical Operators</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/3/4">Loops</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/3/5"><strong>JES:</strong> Adding a Border to a Picture</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/3/6"><strong>JES:</strong> Finding the Predominant Color in a Row</a></p>
</li>
</ol>
</li>
<li><p>Data Containers</p>
<ol>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/4/1">Python Lists</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/4/2">Python Dictionaries</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/4/3">Python Sets</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/4/4"><strong>JES:</strong> Storing User Supplied Data in a Dictionary</a></p>
</li>
</ol>
</li>
<li><p>Functions</p>
<ol>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/5/1">A First Function</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/5/2">Function Return Values</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/5/3">Parameters</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/5/4">Scope of Variables</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/5/5">Pass by Reference or Pass by Value</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/5/6">Sorting with Functions</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/5/7"><strong>JES:</strong> Adding Text (Again) and Saving a File Using Functions</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/5/8"><strong>JES:</strong> Shrinking a Picture</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/5/9"><strong>JES:</strong> Making a Movie with Moving Text</a></p>
</li>
</ol>
</li>
<li><p>Classes</p>
<ol>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/6/1">Classes</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/6/2">Class with Data and Methods</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/6/3">Classes that Interact with Each Other</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/6/4">Inheritance</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook/chapter/6/5"><strong>JES:</strong> Photo Resizing/Rotating Class</a></p>
</li>
</ol>
</li>
</ol>
<p>I hope this animated introduction helps you grasp the fundamental concepts of Python and empowers you to start building your own applications. Dive in, experiment, and let me know what you think! If you have any questions or feedback, I'd love to hear it. Comments and feedback are welcome anytime: <a target="_blank" href="mailto:mark@playbackpress.com">mark@playbackpress.com</a></p>
<p>If you'd like to support my work and help keep Playback Press free for all, consider donating using <a target="_blank" href="https://github.com/sponsors/markm208">GitHub Sponsors</a>. I use all of the donations for hosting costs. Your support helps me continue creating educational content like this. Thank you!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Open Source LLM Agent Handbook: How to Automate Complex Tasks with LangGraph and CrewAI ]]>
                </title>
                <description>
                    <![CDATA[ Ever feel like your AI tools are a bit...well, passive? Like they just sit there, waiting for your next command? Imagine if they could take initiative, break down big problems, and even work together to get things done. That's exactly what LLM agents... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-open-source-llm-agent-handbook/</link>
                <guid isPermaLink="false">683f04aedfb685791a4e8dd2</guid>
                
                    <category>
                        <![CDATA[ llm ]]>
                    </category>
                
                    <category>
                        <![CDATA[ openai ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Open Source ]]>
                    </category>
                
                    <category>
                        <![CDATA[ agentic AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #agent ]]>
                    </category>
                
                    <category>
                        <![CDATA[ agents ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ML ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Bash ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Balajee Asish Brahmandam ]]>
                </dc:creator>
                <pubDate>Tue, 03 Jun 2025 14:20:30 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1748956366197/c4dd2bba-430a-4f12-a3d4-becc6707c52e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Ever feel like your AI tools are a bit...well, passive? Like they just sit there, waiting for your next command? Imagine if they could take initiative, break down big problems, and even work together to get things done.</p>
<p>That's exactly what LLM agents bring to the table. They're changing how we automate complex tasks, and they can help bring our AI ideas to life in a whole new way.</p>
<p>In this article, we'll explore what LLM agents are, how they work, and how you can build your very own using awesome open-source frameworks.</p>
<h3 id="heading-what-well-cover">What we’ll cover:</h3>
<ol>
<li><p><a class="post-section-overview" href="#heading-the-current-state-of-llm-agents">The Current State of LLM Agents</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-from-chatbots-to-autonomous-agents">From Chatbots to Autonomous Agents</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-can-agents-do-today">What Can Agents Do Today?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-whats-available-to-build-with">What's Available to Build With?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-now-is-the-best-time-to-learn">Why Now Is the Best Time to Learn</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-what-are-llm-agents-and-why-are-they-a-big-deal">What Are LLM Agents and Why Are They a Big Deal?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-an-llm">What Is an LLM?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-so-whats-an-llm-agent">So, What’s an LLM Agent?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-does-this-matter">Why Does This Matter?</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-the-rise-of-open-source-agent-frameworks">The Rise of Open-Source Agent Frameworks</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-popular-open-source-agent-frameworks">Popular Open-Source Agent Frameworks</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-these-tools-enable">What These Tools Enable</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-use-a-framework-instead-of-building-from-scratch">Why Use a Framework Instead of Building from Scratch?</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-core-concepts-behind-agent-design">Core Concepts Behind Agent Design</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-the-agent-loop">The Agent Loop</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-key-components-of-an-agent">Key Components of an Agent</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-multi-agent-collaboration">Multi-Agent Collaboration</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-project-automate-your-daily-schedule-from-emails">Project: Automate Your Daily Schedule from Emails</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-were-automating">What We’re Automating</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-1-install-the-required-tools">Step 1: Install the Required Tools</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-define-the-task">Step 2: Define the Task</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-build-the-workflow-with-langgraph">Step 3: Build the Workflow with LangGraph</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-multi-agent-collaboration-with-crewai">Multi-Agent Collaboration with CrewAI</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-crewai">What Is CrewAI?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-sample-roles-for-the-email-summary-task">Sample Roles for the Email Summary Task</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-sample-crewai-code">Sample CrewAI Code</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-what-actually-happens-during-execution">What Actually Happens During Execution?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-are-llm-agents-safe-what-to-know-about-security-and-privacy">Are LLM Agents Safe? What to Know About Security and Privacy</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-troubleshooting-and-tips">Troubleshooting &amp; Tips</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-explore-more-daily-automations">Explore More Daily Automations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-whats-next-in-agent-technology">What’s Next in Agent Technology?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-summary">Final Summary</a></p>
</li>
</ol>
<h2 id="heading-the-current-state-of-llm-agents">The Current State of LLM Agents</h2>
<p>LLM agents are one of the most exciting developments in AI right now. They’re already helping automate real tasks but they’re also still evolving. So where are we today?</p>
<h3 id="heading-from-chatbots-to-autonomous-agents">From Chatbots to Autonomous Agents</h3>
<p>Large Language Models (LLMs) like GPT-4, Claude, Gemini, and LLaMA have evolved from simple chatbots into surprisingly capable reasoning engines. They've gone from answering trivia questions and generating essays to performing complex reasoning, following multi-step instructions, and interacting with tools like web search and code interpreters.</p>
<p>But here’s the catch: these models are <strong>reactive</strong>. They wait for input and give output. They don't retain memory between tasks, plan ahead, or pursue goals on their own. That’s where <strong>LLM agents</strong> come in – they bridge this gap by adding structure, memory, and autonomy.</p>
<h3 id="heading-what-can-agents-do-today">What Can Agents Do Today?</h3>
<p>Right now, LLM agents are already being used for:</p>
<ul>
<li><p>Summarizing emails or documents</p>
</li>
<li><p>Planning daily schedules</p>
</li>
<li><p>Running DevOps scripts</p>
</li>
<li><p>Searching APIs or tools for answers</p>
</li>
<li><p>Collaborating in small “teams” to complete complex tasks</p>
</li>
</ul>
<p>But they’re not perfect yet. Agents can still:</p>
<ul>
<li><p>Get stuck in loops</p>
</li>
<li><p>Misunderstand goals</p>
</li>
<li><p>Require detailed prompts and guardrails</p>
</li>
</ul>
<p>That’s because this technology is still early-stage. Frameworks are getting better fast, but reliability and memory are still works in progress. So just keep that in mind as you experiment.</p>
<h3 id="heading-why-now-is-the-best-time-to-learn">Why Now Is the Best Time to Learn</h3>
<p>The truth is: we’re still early. But not <em>too</em> early.</p>
<p>This is the perfect time to start experimenting with agents:</p>
<ul>
<li><p>The tooling is mature enough to build real projects</p>
</li>
<li><p>The community is growing rapidly</p>
</li>
<li><p>And you don’t need to be an AI expert just comfortable with Python</p>
</li>
</ul>
<h2 id="heading-what-are-llm-agents-and-why-are-they-a-big-deal">What Are LLM Agents and Why Are They a Big Deal?</h2>
<p>Before we dive into the exciting world of agents, let's quickly chat a bit more about the basics.</p>
<h3 id="heading-what-is-an-llm">What Is an LLM?</h3>
<p>An LLM, or Large Language Model, is basically an AI that's learned from a massive amount of text from the internet – think books, articles, code, and tons more. You can picture it as a super-smart autocomplete engine. But it does way more than just finish your sentences. It can also:</p>
<ul>
<li><p>Answer tricky questions</p>
</li>
<li><p>Summarize long articles or documents</p>
</li>
<li><p>Write code, emails, or creative stories</p>
</li>
<li><p>Translate languages instantly</p>
</li>
<li><p>Even solve logic puzzles and have engaging conversations</p>
</li>
</ul>
<p>Chances are you've heard of ChatGPT, which is powered by OpenAI's GPT models. Other popular LLMs you might come across include Claude (from Anthropic), LLaMA (by Meta), Mistral, and Gemini (from Google).</p>
<p>These models work by simply predicting the next word in a sentence based on the context. While that sounds straightforward, when trained on billions of words, LLMs become capable of surprisingly intelligent behavior, understanding your instructions, following step-by-step reasoning, and producing coherent responses across almost any topic you can imagine.</p>
<h3 id="heading-so-whats-an-llm-agent">So, What’s an LLM Agent?</h3>
<p>While LLMs are super powerful, they usually just <em>react –</em> they only respond when you ask them something. An LLM agent, on the other hand, is <em>proactive</em>.</p>
<p>LLM agents can:</p>
<ul>
<li><p>Break down big, complex tasks into smaller, manageable steps</p>
</li>
<li><p>Make smart decisions and figure out what to do next</p>
</li>
<li><p>Use "tools" like web search, calculators, or even other apps</p>
</li>
<li><p>Work towards a goal, even if it takes multiple steps or tries</p>
</li>
<li><p>Team up with other agents to accomplish shared objectives</p>
</li>
</ul>
<p>In short, LLM agents can think, plan, act, and adapt.</p>
<p>Think of an LLM agent like your super-efficient new assistant: you give it a goal, and it figures out how to achieve it all on its own.</p>
<h3 id="heading-why-does-this-matter">Why Does This Matter?</h3>
<p>This shift from just responding to actively pursuing goals opens a ton of exciting possibilities:</p>
<ul>
<li><p>Automating boring IT or DevOps tasks</p>
</li>
<li><p>Generating detailed reports from raw data</p>
</li>
<li><p>Helping you with multi-step research projects</p>
</li>
<li><p>Reading through your daily emails and highlighting key info</p>
</li>
<li><p>Running your internal tools to take real-world actions</p>
</li>
</ul>
<p>Unlike older, rule-based bots, LLM agents can reason, reflect, and learn from their attempts. This makes them a much better fit for real-world tasks that are messy, require flexibility, and depend on understanding context.</p>
<h2 id="heading-the-rise-of-open-source-agent-frameworks">The Rise of Open-Source Agent Frameworks</h2>
<p>Not too long ago, if you wanted to build an AI system that could act autonomously, it meant writing a ton of custom code, painstakingly managing memory, and trying to stitch together dozens of components. It was a complex, delicate, and highly specialized job.</p>
<p>But guess what? That's not the case anymore.</p>
<p>In 2024, a wave of fantastic open-source frameworks hit the scene. These tools have made it dramatically easier to build powerful LLM agents without you having to reinvent the wheel every time.</p>
<h3 id="heading-popular-open-source-agent-frameworks">Popular Open-Source Agent Frameworks</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Framework</strong></td><td><strong>Description</strong></td><td><strong>Maintainer</strong></td></tr>
</thead>
<tbody>
<tr>
<td>LangGraph</td><td>Graph-based framework for agent state and memory</td><td>LangChain</td></tr>
<tr>
<td>CrewAI</td><td>"Role-based, multi-agent collaboration engine"</td><td>Community (CrewAI)</td></tr>
<tr>
<td>AutoGen</td><td>Customizable multi-agent chat orchestration</td><td>Microsoft</td></tr>
<tr>
<td>AgentVerse</td><td>Modular framework for agent simulation and testing</td><td>Open-source project</td></tr>
</tbody>
</table>
</div><h3 id="heading-what-these-tools-enable">What These Tools Enable</h3>
<p>These frameworks give you ready-made building blocks to handle the trickier parts of creating agents:</p>
<ul>
<li><p><strong>Planning</strong> – Letting agents decide their next move</p>
</li>
<li><p><strong>Tool Use</strong> – Easily connecting agents to things like file systems, web browsers, APIs, or databases</p>
</li>
<li><p><strong>Memory</strong> – Storing and retrieving past information or intermediate results for long-term context</p>
</li>
<li><p><strong>Multi-Agent Collaboration</strong> – Setting up teams of agents that work together on shared goals</p>
</li>
</ul>
<h3 id="heading-why-use-a-framework-instead-of-building-from-scratch">Why Use a Framework Instead of Building from Scratch?</h3>
<p>While you <em>could</em> build a custom agent from the ground up, using a framework will save you a huge amount of time and effort. Open-source agent libraries come packed with:</p>
<ul>
<li><p>Built-in support for orchestrating LLMs</p>
</li>
<li><p>Proven patterns for task planning, keeping track of where you are, and getting feedback</p>
</li>
<li><p>Easy integration with popular models like OpenAI, or even models you run locally</p>
</li>
<li><p>The flexibility to grow from a single helpful agent to entire teams of agents</p>
</li>
</ul>
<p>Basically, these frameworks let you focus on <strong>what your agent should do</strong>, rather than getting bogged down in how to build all the internal workings. Plus, choosing open source means you benefit from community contributions, transparency in how they work, and the freedom to tweak them to your exact needs, without getting locked into a single vendor.</p>
<h2 id="heading-core-concepts-behind-agent-design">Core Concepts Behind Agent Design</h2>
<p>To really grasp how LLM agents operate, it helps to think of them as goal-driven systems that constantly cycle through observing, reasoning, and acting. This continuous loop allows them to tackle tasks that go beyond simple questions and answers, moving into true automation, tool usage, and adapting on the fly.</p>
<h3 id="heading-the-agent-loop">The Agent Loop</h3>
<p>Most LLM agents function based on a mental model called the <strong>Agent Loop</strong> a step-by-step cycle that repeats until the job is done. Here’s how it typically works:</p>
<ul>
<li><p><strong>Perceive:</strong> The agent starts by noticing something in its environment or receiving new information. This could be your prompt, a piece of data, or the current state of a system.</p>
</li>
<li><p><strong>Plan:</strong> Based on what it perceives and its overall goal, the agent decides what to do next. It might break the task into smaller sub-goals or figure out the best tool for the job.</p>
</li>
<li><p><strong>Act:</strong> The agent then acts. This could mean running a function, calling an API, searching the web, interacting with a database, or even asking another agent for help.</p>
</li>
<li><p><strong>Reflect:</strong> After acting, the agent looks at the outcome: Did it work? Was the result useful? Should it try a different approach? Based on this, it updates its plan and keeps going until the task is complete.</p>
</li>
</ul>
<p>This loop is what makes agents so dynamic. It allows them to handle ever-changing tasks, learn from partial results, and correct their course qualities that are vital for building truly useful AI assistants.</p>
<h3 id="heading-key-components-of-an-agent">Key Components of an Agent</h3>
<p>To do their job effectively, agents are built around several crucial parts:</p>
<ul>
<li><p><strong>Tools</strong> are how an agent interacts with the real (or digital) world. These can be anything from search engines, code execution environments, file readers, or API clients, to simple calculators or command-line scripts.</p>
</li>
<li><p><strong>Memory</strong> lets agents remember what they've done or seen across different steps. This might include previous things you've said, temporary results, or key decisions. Some frameworks offer short-term memory (just for one session), while others support long-term memory that can span multiple sessions or goals.</p>
</li>
<li><p><strong>Environment</strong> refers to the external data or system context the agent operates within think APIs, documents, databases, files, or sensor inputs. The more information and access an agent have to its environment, the more meaningful actions it can take.</p>
</li>
<li><p><strong>Goal</strong> is the agent's ultimate objective: what it's trying to achieve. Goals should be specific and clear for instance, “generate a daily schedule,” “summarize this document,” or “extract tasks from emails.”</p>
</li>
</ul>
<h3 id="heading-multi-agent-collaboration">Multi-Agent Collaboration</h3>
<p>For more advanced systems, you can even have multiple agents working together to hit a shared target. Each agent can be given a specific <strong>role</strong> that highlights its specialty just like people working on a team.</p>
<p>For example:</p>
<ul>
<li><p>A <strong>researcher agent</strong> might be tasked with gathering information.</p>
</li>
<li><p>A <strong>coder agent</strong> could write Python scripts or automation routines.</p>
</li>
<li><p>A <strong>reviewer agent</strong> might check the results and ensure everything is up to snuff.</p>
</li>
</ul>
<p>These agents can chat with each other, share information, and even debate or vote on decisions. This kind of teamwork allows AI systems to tackle bigger, more complex tasks while keeping things organized and modular.</p>
<h2 id="heading-project-automate-your-daily-schedule-from-emails">Project: Automate Your Daily Schedule from Emails</h2>
<h3 id="heading-what-were-automating">What We’re Automating</h3>
<p>Think about your typical morning routine:</p>
<ul>
<li><p>You open your inbox.</p>
</li>
<li><p>You quickly scan through a bunch of emails.</p>
</li>
<li><p>You try to spot meetings, tasks, and important reminders.</p>
</li>
<li><p>Then, you manually write a to-do list or add things to your calendar.</p>
</li>
</ul>
<p>Let's use an LLM agent to make that process effortless. Our agent will:</p>
<ul>
<li><p>Read a list of your email messages</p>
</li>
<li><p>Pull out time-sensitive items like meetings or deadlines</p>
</li>
<li><p>Summarize everything into a nice, clean daily schedule</p>
</li>
</ul>
<h3 id="heading-step-1-install-the-required-tools">Step 1: Install the Required Tools</h3>
<p>To get started, you'll need three main tools: Python, VSCode, and an OpenAI API key.</p>
<h4 id="heading-1-install-python-39-or-higher">1. Install Python 3.9 or Higher</h4>
<p>Grab the latest version of Python 3.9+ from the official website: <a target="_blank" href="https://www.python.org/downloads/">https://www.python.org/downloads/</a></p>
<p>Once it's installed, double-check it by running <code>python --version</code> in your terminal.</p>
<p>This command simply asks your system to report the Python version currently installed. You'll want to see Python 3.9.x or something higher to ensure compatibility with our project.</p>
<h4 id="heading-2-install-vscode-optional-but-recommended">2. Install VSCode (Optional but Recommended)</h4>
<p>VSCode is a fantastic, user-friendly code editor that works perfectly with Python. You can download it right here: <a target="_blank" href="https://code.visualstudio.com/">https://code.visualstudio.com/</a>.</p>
<h4 id="heading-3-get-your-openai-api-key">3. Get Your OpenAI API Key</h4>
<p>Head over to: https://platform.openai.com</p>
<p>Sign in or create a new account. Navigate to your API Keys page. Click “Create new secret key” and make sure to copy that key somewhere safe for later.</p>
<h4 id="heading-4-install-python-libraries">4. Install Python Libraries</h4>
<p>Open your terminal or command prompt and install these essential packages:</p>
<pre><code class="lang-bash">pip install langgraph langchain openai
</code></pre>
<p>This command uses pip, Python's package manager, to download and install three crucial libraries for our agent:</p>
<ul>
<li><p>langgraph: The core framework we'll use to build our agent's workflow.</p>
</li>
<li><p>langchain: A foundational library for working with large language models, upon which LangGraph is built.</p>
</li>
<li><p>openai: The official Python library for connecting to OpenAI's powerful AI models.</p>
</li>
</ul>
<p>If you're excited to try out multi-agent setups (which we'll cover in Step 5), also install CrewAI:</p>
<pre><code class="lang-bash">pip install crewai
</code></pre>
<p>This command installs CrewAI, a specialized framework that makes it easy to orchestrate multiple AI agents working together as a team.</p>
<p><strong>5. Set Your OpenAI API Key</strong></p>
<p>You need to make sure your Python code can find and use your OpenAI API key. This is typically done by setting it as an environment variable.</p>
<p>On macOS/Linux, run this in your terminal (replace "your-api-key" with your actual key):</p>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> OPENAI_API_KEY=<span class="hljs-string">"your-api-key"</span>
</code></pre>
<p>This command sets an environment variable named OPENAI_API_KEY. Environment variables are a secure way for applications (like your Python script) to access sensitive information without hardcoding it directly into the code itself.</p>
<p>On Windows (using Command Prompt), do this:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">set</span> OPENAI_API_KEY=<span class="hljs-string">"your-api-key"</span>
</code></pre>
<p>This is the Windows equivalent command to set the <code>OPENAI_API_KEY</code> environment variable.</p>
<p>Now, your Python code will be all set to talk to the OpenAI model!</p>
<h3 id="heading-step-2-define-the-task">Step 2: Define the Task</h3>
<p>We discussed this briefly in the beginning of this section. But to reiterate, this is what we’ll want our agent to do:</p>
<ul>
<li><p>Scan for meetings, events, and important tasks.</p>
</li>
<li><p>Jot them down quickly in a notebook or an app.</p>
</li>
<li><p>Create a rough mental plan for your day.</p>
</li>
</ul>
<p>This routine takes time and mental energy. So having an agent do it for us will be super helpful.</p>
<h3 id="heading-step-3-build-the-workflow-with-langgraph">Step 3: Build the Workflow with LangGraph</h3>
<h4 id="heading-what-is-langgraph">What Is LangGraph?</h4>
<p>LangGraph is a cool framework that helps you build agents using a "graph-based" workflow, kind of like drawing a flowchart. It's powered by LangChain and gives you a lot more control over exactly how each step in your agent's process unfolds.</p>
<p>Each "node" in this graph represents a decision point or a function that:</p>
<ul>
<li><p>Takes some input (its current "state").</p>
</li>
<li><p>Does some reasoning or takes an action (often involving the LLM and its tools).</p>
</li>
<li><p>Returns an updated output (a new "state").</p>
</li>
</ul>
<p>You draw the connections between these nodes, and LangGraph then executes it like a smart, automated state machine.</p>
<h4 id="heading-why-use-langgraph">Why Use LangGraph?</h4>
<ul>
<li><p>You get to control the precise order of execution.</p>
</li>
<li><p>It's fantastic for building workflows that have multiple steps or even branch off into different paths.</p>
</li>
<li><p>It plays nicely with both cloud-based models (like OpenAI) and models you run locally.</p>
</li>
</ul>
<p>Alright – now let’s write the code.</p>
<h5 id="heading-1-simulate-email-input"><strong>1. Simulate Email Input</strong></h5>
<p>In a real application, your agent would probably connect to Gmail or Outlook to fetch your actual emails. For this example, though, we’ll just hardcode some sample messages to keep things simple:</p>
<pre><code class="lang-python">Python

emails = <span class="hljs-string">"""
1. Subject: Standup Call at 10 AM
2. Subject: Client Review due by 5 PM
3. Subject: Lunch with Sarah at noon
4. Subject: AWS Budget Warning – 80% usage
5. Subject: Dentist Appointment - 4 PM
"""</span>
</code></pre>
<p>This multiline Python string, <code>emails</code>, acts as our stand-in for real email content. We're providing a simple, structured list of email subjects to demonstrate how the agent will process text.</p>
<h5 id="heading-2-define-the-agent-logic"><strong>2. Define the Agent Logic</strong></h5>
<p>Now, we'll tell OpenAI’s GPT model how to process this email text and turn it into a summary.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> langchain_openai <span class="hljs-keyword">import</span> ChatOpenAI
<span class="hljs-keyword">from</span> langgraph.graph <span class="hljs-keyword">import</span> StateGraph, END
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> TypedDict, Annotated, List
<span class="hljs-keyword">import</span> operator

<span class="hljs-comment"># Define the state for our graph</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AgentState</span>(<span class="hljs-params">TypedDict</span>):</span>
    emails: str
    result: str

llm = ChatOpenAI(temperature=<span class="hljs-number">0</span>, model=<span class="hljs-string">"gpt-4o"</span>) <span class="hljs-comment"># Using gpt-4o for better performance</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">calendar_summary_agent</span>(<span class="hljs-params">state: AgentState</span>) -&gt; AgentState:</span>
    emails = state[<span class="hljs-string">"emails"</span>]
    prompt = <span class="hljs-string">f"Summarize today's schedule based on these emails, listing time-sensitive items first and then other important notes. Be concise and use bullet points:\n<span class="hljs-subst">{emails}</span>"</span>
    summary = llm.invoke(prompt).content
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"result"</span>: summary, <span class="hljs-string">"emails"</span>: emails} <span class="hljs-comment"># Ensure emails is also returned</span>
</code></pre>
<p>Here’s what’s going on:</p>
<ul>
<li><p><strong>Imports</strong>: We bring in necessary components:</p>
<ul>
<li><p><code>ChatOpenAI</code> to connect to the LLM,</p>
</li>
<li><p><code>StateGraph</code> and <code>END</code> from <code>langgraph.graph</code> to build our agent workflow,</p>
</li>
<li><p><code>TypedDict</code>, <code>Annotated</code>, and <code>List</code> from <code>typing</code> for type checking and structure,</p>
</li>
<li><p><code>operator</code> (though not used in this snippet, it can help with comparisons or logic).</p>
</li>
</ul>
</li>
<li><p><strong>AgentState</strong>: This <code>TypedDict</code> defines the shape of the data our agent will work with. It includes:</p>
<ul>
<li><p><code>emails</code>: the raw input messages.</p>
</li>
<li><p><code>result</code>: the final output (the daily summary).</p>
</li>
</ul>
</li>
<li><p><strong>llm = ChatOpenAI(...)</strong>: Initializes the language model. We're using GPT-4o with <code>temperature=0</code> to ensure consistent, predictable output perfect for structured summarization tasks.</p>
</li>
<li><p><strong>calendar_summary_agent(state: AgentState)</strong>: This function is the "brain" of our agent. It:</p>
<ul>
<li><p>Takes in the current state, which includes a list of emails.</p>
</li>
<li><p>Extracts the emails from that state.</p>
</li>
<li><p>Constructs a prompt that tells the model to generate a concise daily schedule summary using bullet points, prioritizing time-sensitive items.</p>
</li>
<li><p>Sends this prompt to the model with <code>llm.invoke(prompt).content</code>, which returns the LLM’s response as plain text.</p>
</li>
<li><p>Returns a new <code>AgentState</code> dictionary containing:</p>
<ul>
<li><p><code>result</code>: the generated summary,</p>
</li>
<li><p><code>emails</code>: preserved in case we need it downstream.</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h5 id="heading-3-build-and-run-the-graph"><strong>3. Build and Run the Graph</strong></h5>
<p>Now, let's use LangGraph to map out the flow of our single-agent task and then run it.</p>
<pre><code class="lang-python">builder = StateGraph(AgentState)
builder.add_node(<span class="hljs-string">"calendar"</span>, calendar_summary_agent)
builder.set_entry_point(<span class="hljs-string">"calendar"</span>)
builder.set_finish_point(<span class="hljs-string">"calendar"</span>) <span class="hljs-comment"># END is implicit if not set explicitly</span>

graph = builder.compile()

<span class="hljs-comment"># Run the graph using your simulated email data</span>
result = graph.invoke({<span class="hljs-string">"emails"</span>: emails})
print(result[<span class="hljs-string">"result"</span>])
</code></pre>
<p>Here’s what’s going on:</p>
<ul>
<li><p><strong>builder = StateGraph(AgentState):</strong> We're initiating a StateGraph object. By passing AgentState, we're telling LangGraph the expected data structure for its internal state.</p>
</li>
<li><p><strong>builder.add_node("calendar", calendar_summary_agent):</strong> This line adds a named "node" to our graph. We're calling it "calendar", and we're linking it to our <code>calendar_summary_agent</code> function, meaning that function will be executed when this node is active.</p>
</li>
<li><p><strong>builder.set_entry_point("calendar"):</strong> This sets "calendar" as the very first step in our workflow. When we start the graph, execution will begin here.</p>
</li>
<li><p><strong>builder.set_finish_point("calendar"):</strong> This tells LangGraph that once the "calendar" node finishes its job, the entire graph process is complete.</p>
</li>
<li><p><strong>graph = builder.compile():</strong> This command takes our defined graph blueprint and "compiles" it into an executable workflow.</p>
</li>
<li><p><strong>result = graph.invoke({"emails": emails}):</strong> This is where the magic happens! We're telling our graph to start running. We pass it an initial state that contains our emails data. The graph will then process this data through its nodes until it reaches an end point, returning the final state.</p>
</li>
<li><p><strong>print(result["result"]):</strong> Finally, we grab the summarized schedule from the result (the final state of our graph) and print it to the console.</p>
</li>
</ul>
<h4 id="heading-example-output">Example Output</h4>
<p><code>Your Schedule:</code><br><code>- 10:00 AM – Standup Call</code><br><code>- 12:00 PM – Lunch with Sarah</code><br><code>- 4:00 PM – Dentist Appointment</code><br><code>- Submit client report by 5:00 PM</code><br><code>- AWS Budget Warning – check usage</code></p>
<p>Boom! You've just built an AI agent that can read your emails and whip up your daily schedule. Pretty cool, right? This is a simple yet powerful peek into what LLM agents can do with just a few lines of code.</p>
<h2 id="heading-multi-agent-collaboration-with-crewai">Multi-Agent Collaboration with CrewAI</h2>
<h3 id="heading-what-is-crewai">What Is CrewAI?</h3>
<p>CrewAI is an exciting open-source framework that lets you build <em>teams</em> of agents that work together seamlessly just like a real-world project team! Each agent in a CrewAI setup:</p>
<ul>
<li><p>Has a specific, specialized role.</p>
</li>
<li><p>Can communicate and share information with its teammates.</p>
</li>
<li><p>Collaborates to achieve a shared goal.</p>
</li>
</ul>
<p>This multi-agent approach is super useful when your task is too big or too complex for just one agent, or when breaking it down into specialized parts makes it clearer and more efficient.</p>
<h3 id="heading-sample-roles-for-the-email-summary-task">Sample Roles for the Email Summary Task</h3>
<p>Let's imagine our email summary task being handled by a small team of agents:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Agent Name</strong></td><td><strong>Role</strong></td><td><strong>Responsibility</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Extractor</td><td>Email Scanner</td><td>"Find meetings, reminders, and tasks from emails"</td></tr>
<tr>
<td>Prioritizer</td><td>Schedule Optimizer</td><td>Sort items by urgency and time</td></tr>
<tr>
<td>Formatter</td><td>Output Generator</td><td>"Write a clean, polished daily agenda"</td></tr>
</tbody>
</table>
</div><h3 id="heading-sample-crewai-code">Sample CrewAI Code</h3>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> crewai <span class="hljs-keyword">import</span> Agent, Crew, Task, Process
<span class="hljs-keyword">from</span> langchain_openai <span class="hljs-keyword">import</span> ChatOpenAI
<span class="hljs-keyword">import</span> os

<span class="hljs-comment"># Set your OpenAI API key from environment variables</span>
<span class="hljs-comment"># os.environ["OPENAI_API_KEY"] = "YOUR_API_KEY" # Make sure this is set, or defined directly</span>

<span class="hljs-comment"># Initialize the LLM (using gpt-4o for better performance)</span>
llm = ChatOpenAI(temperature=<span class="hljs-number">0</span>, model=<span class="hljs-string">"gpt-4o"</span>)

<span class="hljs-comment"># Define the agents with specific roles and goals</span>
extractor = Agent(
    role=<span class="hljs-string">"Email Scanner"</span>,
    goal=<span class="hljs-string">"Find all meetings, reminders, and tasks from the given emails, accurately extracting details like time, date, and subject."</span>,
    backstory=<span class="hljs-string">"You are an expert at scanning emails for key information. You meticulously extract every relevant detail."</span>,
    verbose=<span class="hljs-literal">True</span>,
    allow_delegation=<span class="hljs-literal">False</span>,
    llm=llm
)

prioritizer = Agent(
    role=<span class="hljs-string">"Schedule Optimizer"</span>,
    goal=<span class="hljs-string">"Sort extracted items by urgency and time, preparing them for a daily agenda."</span>,
    backstory=<span class="hljs-string">"You are a master of time management, always knowing what needs to be done first. You organize tasks logically."</span>,
    verbose=<span class="hljs-literal">True</span>,
    allow_delegation=<span class="hljs-literal">False</span>,
    llm=llm
)

formatter = Agent(
    role=<span class="hljs-string">"Output Generator"</span>,
    goal=<span class="hljs-string">"Generate a clean, polished, and concise daily agenda in bullet-point format, clearly listing all schedule items."</span>,
    backstory=<span class="hljs-string">"You are a professional secretary, ensuring all outputs are perfectly formatted and easy to read. You prioritize clarity."</span>,
    verbose=<span class="hljs-literal">True</span>,
    allow_delegation=<span class="hljs-literal">False</span>,
    llm=llm
)

<span class="hljs-comment"># Simulate email input</span>
emails = <span class="hljs-string">"""
1. Subject: Standup Call at 10 AM
2. Subject: Client Review due by 5 PM
3. Subject: Lunch with Sarah at noon
4. Subject: AWS Budget Warning – 80% usage
5. Subject: Dentist Appointment - 4 PM
"""</span>

<span class="hljs-comment"># Define the tasks for each agent</span>
extract_task = Task(
    description=<span class="hljs-string">f"Extract all relevant events, meetings, and tasks from these emails: <span class="hljs-subst">{emails}</span>. Focus on precise details."</span>,
    agent=extractor,
    expected_output=<span class="hljs-string">"A list of extracted items with their details (e.g., '- Standup Call at 10 AM', '- Client Review due by 5 PM')."</span>
)

prioritize_task = Task(
    description=<span class="hljs-string">"Prioritize the extracted items by time and urgency. Meetings first, then deadlines, then other notes."</span>,
    agent=prioritizer,
    context=[extract_task], <span class="hljs-comment"># The output of extract_task is the input here</span>
    expected_output=<span class="hljs-string">"A prioritized list of schedule items."</span>
)

format_task = Task(
    description=<span class="hljs-string">"Format the prioritized schedule into a clean, easy-to-read daily agenda using bullet points. Ensure concise language."</span>,
    agent=formatter,
    context=[prioritize_task], <span class="hljs-comment"># The output of prioritize_task is the input here</span>
    expected_output=<span class="hljs-string">"A well-formatted daily agenda with bullet points."</span>
)

<span class="hljs-comment"># Instantiate the crew</span>
crew = Crew(
    agents=[extractor, prioritizer, formatter],
    tasks=[extract_task, prioritize_task, format_task],
    process=Process.sequential, <span class="hljs-comment"># Tasks are executed sequentially</span>
    verbose=<span class="hljs-number">2</span> <span class="hljs-comment"># Outputs more details during execution</span>
)

<span class="hljs-comment"># Run the crew</span>
result = crew.kickoff()
print(<span class="hljs-string">"\n########################"</span>)
print(<span class="hljs-string">"## Final Daily Agenda ##"</span>)
print(<span class="hljs-string">"########################\n"</span>)
print(result)
</code></pre>
<p>Here’s what’s going on:</p>
<ul>
<li><p><strong>Imports:</strong> We bring in key classes from CrewAI: Agent, Crew, Task, and Process. We also import <code>ChatOpenAI</code> for our language model and os to handle environment variables.</p>
</li>
<li><p><strong>llm = ChatOpenAI(...):</strong> Just like in the LangGraph example, this sets up our OpenAI language model, making sure its responses are direct (temperature=0) and using the gpt-4o model.</p>
</li>
<li><p><strong>Agent Definitions (extractor, prioritizer, formatter):</strong></p>
<ul>
<li><p>Each of these variables creates an Agent instance. An agent is defined by its role (what it does), a specific goal it's trying to achieve, and a backstory (a sort of personality or expertise that helps the LLM understand its purpose better).</p>
</li>
<li><p>verbose=True is super helpful for debugging, as it makes the agents print out their "thoughts" as they work.</p>
</li>
<li><p>allow_delegation=False means these agents won't pass their assigned tasks to other agents (though this can be set to True for more complex delegation scenarios).</p>
</li>
<li><p>llm=llm connects each agent to our OpenAI language model.</p>
</li>
</ul>
</li>
<li><p><strong>Simulated emails:</strong> We reuse the same sample email data for this example.</p>
</li>
<li><p><strong>Task Definitions (extract_task, prioritize_task, format_task):</strong></p>
<ul>
<li><p>Each Task defines a specific piece of work that an agent needs to perform.</p>
</li>
<li><p>description clearly tells the agent what the task involves.</p>
</li>
<li><p>agent assigns this task to one of our defined agents (e.g., extractor for extract_task).</p>
</li>
<li><p>context=[...] is a critical part of CrewAI's collaboration. It tells a task to use the <em>output</em> of a previous task as its <em>input</em>. For instance, prioritize_task takes the extract_task's output as its context.</p>
</li>
<li><p>expected_output gives the agent an idea of what its result should look like, helping guide the LLM.</p>
</li>
</ul>
</li>
<li><p><strong>crew = Crew(...):</strong></p>
<ul>
<li><p>This is where we assemble our team! We create a Crew instance, giving it our list of agents and tasks.</p>
</li>
<li><p>process=Process.sequential tells the crew to execute tasks one after another in the order they're defined in the tasks list. CrewAI also supports more advanced processes like hierarchical ones.</p>
</li>
<li><p>verbose=2 will show you a very detailed log of the crew's internal workings and communication.</p>
</li>
</ul>
</li>
<li><p><strong>result = crew.kickoff():</strong> This command officially starts the entire multi-agent workflow. The agents will begin collaborating, passing information, and working through their assigned tasks in sequence.</p>
</li>
<li><p><strong>fprint(result):</strong> Finally, the consolidated output from the entire crew's collaborative effort is printed to your console.</p>
</li>
</ul>
<p>CrewAI cleverly handles all the communication between agents, figures out who needs to work on what and when, and passes the output smoothly from one agent to the next it's like having a mini AI assembly line!</p>
<h2 id="heading-what-actually-happens-during-execution">What Actually Happens During Execution?</h2>
<p>So, whether you're using LangGraph or CrewAI, what's really going on behind the scenes when an agent runs? Let's break down the execution process:</p>
<ul>
<li><p>The system gets an <strong>input state</strong> (for example, your emails).</p>
</li>
<li><p>The first agent or graph node reads this input and uses a <strong>Large Language Model (LLM)</strong> to make sense of it.</p>
</li>
<li><p>Based on its understanding, the agent decides on an <strong>action</strong> like pulling out key events or calling a specific tool.</p>
</li>
<li><p>If needed, the agent might <strong>invoke tools</strong> (like a web search or a file reader) to get more context or perform external operations.</p>
</li>
<li><p>The result of that action is then <strong>passed to the next agent</strong> in the team (if it's a multi-agent setup) or returned directly to you.</p>
</li>
</ul>
<p>Execution keeps going until:</p>
<ul>
<li><p>The task is fully completed.</p>
</li>
<li><p>All agents have finished their assigned roles.</p>
</li>
<li><p>A stopping condition or a designated "END" point in the workflow is reached.</p>
</li>
</ul>
<p>Think of this as a super-smart workflow engine where every single step involves reasoning, making decisions, and remembering previous interactions.</p>
<h2 id="heading-are-llm-agents-safe-what-to-know-about-security-and-privacy">Are LLM Agents Safe? What to Know About Security and Privacy</h2>
<p>As cool as LLM agents are, they raise an important question: <em>can you really trust an AI to run parts of your workflow or interact with your data?</em> It depends. If you’re using services like OpenAI or Anthropic, your data is encrypted in transit and (as of now) isn’t used for training.</p>
<p>But some data might still be temporarily logged to prevent abuse. That’s usually fine for testing and personal projects, but if you’re working with sensitive business info, customer data, or anything private, you’ll want to be careful.</p>
<p>Use anonymized inputs, avoid exposing full datasets, and consider running agents locally using open-source models like LLaMA or Mistral if full control matters to you.</p>
<p>You can also set clear boundaries for your agents so they don’t overstep. Think of it like onboarding a new intern: you wouldn’t give them access to everything on day one.</p>
<p>Give agents only the tools and files they need, keep logs of what they do, and always review the results before letting them make real changes.</p>
<p>As this tech grows, more safety features are coming like better sandboxing, memory limits, and role-based access. But for now, it’s smart to treat your agents like powerful helpers that still need some human supervision.</p>
<h2 id="heading-troubleshooting-amp-tips">Troubleshooting &amp; Tips</h2>
<p>Sometimes, agents can be a bit quirky! Here are some common issues you might run into and how to fix them:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Issue</strong></td><td><strong>Suggested Fix</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Agent seems to loop forever</td><td>Set a maximum number of iterations or define a clearer stopping point.</td></tr>
<tr>
<td>Output is too chatty or verbose</td><td>Use more specific prompts (for example, “Respond in bullet points only”).</td></tr>
<tr>
<td>Input is too long or gets cut off</td><td>Break down large pieces of content into smaller chunks and summarize them individually.</td></tr>
<tr>
<td>Agent runs too slowly</td><td>Try using a faster LLM model like gpt-3.5 or consider running a local model.</td></tr>
</tbody>
</table>
</div><p>A handy tip: You can also add print() statements or logging messages inside your agent functions to see what's happening at each stage and debug state transitions.</p>
<h2 id="heading-explore-more-daily-automations">Explore More Daily Automations</h2>
<p>Once you've built one agent-based task, you'll find it incredibly easy to adapt the pattern for other automations. Here are some cool ideas to get your creative juices flowing:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Task Type</strong></td><td><strong>Example Automation</strong></td></tr>
</thead>
<tbody>
<tr>
<td>DevOps Assistant</td><td>"Read system logs, detect potential issues, and suggest solutions."</td></tr>
<tr>
<td>Finance Tracker</td><td>Read bank statements or CSV files and summarize your spending habits/budgets.</td></tr>
<tr>
<td>Meeting Organizer</td><td>After a meeting, automatically extract action items and assign owners.</td></tr>
<tr>
<td>Inbox Cleaner</td><td>"Automatically label, archive, and delete non-urgent emails."</td></tr>
<tr>
<td>Note Summarizer</td><td>Convert your daily notes into a neatly formatted to-do list or summary.</td></tr>
<tr>
<td>Link Checker</td><td>Extract URLs from documents and automatically test if they're still valid.</td></tr>
<tr>
<td>Resume Formatter</td><td>Score resumes against job descriptions and format them automatically.</td></tr>
</tbody>
</table>
</div><p>Each of these can be built using the very same principles and frameworks we discussed whether that's LangGraph or CrewAI.</p>
<h2 id="heading-whats-next-in-agent-technology">What’s Next in Agent Technology?</h2>
<p>LLM agents are evolving at lightning speed, and the next wave of innovation is already here:</p>
<ul>
<li><p><strong>Smarter memory systems</strong>: Expect agents to have better long-term memory, allowing them to learn over extended periods and remember past conversations and actions.</p>
</li>
<li><p><strong>Multi-modal agents</strong>: Agents won't just handle text anymore! They'll be able to process and understand images, audio, and video, making them much more versatile.</p>
</li>
<li><p><strong>Advanced planning frameworks</strong>: Techniques like ReAct, Toolformer, and AutoGen are constantly improving agents' ability to reason, plan, and reduce those pesky "hallucinations."</p>
</li>
<li><p><strong>Edge deployment</strong>: Imagine agents running entirely offline on your local computer or device using lightweight models like LLaMA 3 or Mistral.</p>
</li>
</ul>
<p>In the very near future, you'll see agents seamlessly integrated into:</p>
<ul>
<li><p>Your DevOps pipelines</p>
</li>
<li><p>Big enterprise workflows</p>
</li>
<li><p>Everyday productivity tools</p>
</li>
<li><p>Mobile apps and smart devices</p>
</li>
<li><p>Games, simulations, and educational platforms</p>
</li>
</ul>
<h2 id="heading-final-summary">Final Summary</h2>
<p>Alright, let's quickly recap all the cool stuff you've just learned and accomplished:</p>
<ul>
<li><p>You've gotten a solid grasp of what LLM agents are and why they're so powerful.</p>
</li>
<li><p>You've seen how open-source frameworks like LangGraph and CrewAI make building agents much easier.</p>
</li>
<li><p>You've built a real LLM agent using LangGraph to automate a common daily task: summarizing your inbox!</p>
</li>
<li><p>You've explored the world of multi-agent collaboration with CrewAI, understanding how teams of AIs can work together.</p>
</li>
<li><p>You've learned how to take these principles and scale them to automate countless other tasks.</p>
</li>
</ul>
<p>So, next time you find yourself stuck doing something repetitive, just ask yourself: "Hey, can I build an agent for that?" The answer is probably yes!</p>
<h3 id="heading-resources-recap">Resources Recap</h3>
<p>Here are some helpful resources if you want to dive deeper into building LLM agents:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Resource</strong></td><td><strong>Link</strong></td></tr>
</thead>
<tbody>
<tr>
<td>LangGraph Docs</td><td><a target="_blank" href="https://docs.langgraph.dev/">https://docs.langgraph.dev/</a></td></tr>
<tr>
<td>CrewAI GitHub</td><td><a target="_blank" href="https://github.com/joaomdmoura/crewAI">https://github.com/joaomdmoura/crewAI</a></td></tr>
<tr>
<td>LangChain Docs</td><td><a target="_blank" href="https://docs.langchain.com/docs/">https://docs.langchain.com/docs/</a></td></tr>
<tr>
<td>OpenAI API Docs</td><td><a target="_blank" href="https://platform.openai.com/docs">https://platform.openai.com/docs</a></td></tr>
<tr>
<td>Python 3.9+</td><td><a target="_blank" href="https://www.python.org/downloads/">https://www.python.org/downloads/</a></td></tr>
<tr>
<td>VSCode</td><td><a target="_blank" href="https://code.visualstudio.com/">https://code.visualstudio.com/</a></td></tr>
</tbody>
</table>
</div> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create Documentation with docs.page – A Beginner's Tutorial ]]>
                </title>
                <description>
                    <![CDATA[ One of the most tedious tasks for every startup, company, and open-source project is often building and managing documentation – especially for medium to large-scale documentation websites. docs.page is an open-source documentation tool that helps yo... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-documentation-with-docspage/</link>
                <guid isPermaLink="false">681a558b9791d0e469b84519</guid>
                
                    <category>
                        <![CDATA[ documentation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Rajdeep Singh ]]>
                </dc:creator>
                <pubDate>Tue, 06 May 2025 18:31:39 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746471569068/23f70d3e-a76e-4287-a6a9-579c23a4fcb2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>One of the most tedious tasks for every startup, company, and open-source project is often building and managing documentation – especially for medium to large-scale documentation websites.</p>
<p><a target="_blank" href="http://docs.page"><strong>docs.page</strong></a> is an open-source documentation tool that helps you create instant, fast, beautiful, and responsive documentation websites with minimal configuration. It is an open-source project developed by Invertase, a company known for creating developer tools and SDKs.</p>
<p>docs.page is designed to streamline the process of publishing documentation by sourcing content directly from public GitHub repositories.</p>
<h3 id="heading-key-features">Key Features:</h3>
<ul>
<li><p>Zero configuration: you create a 'docs.json' file and a 'docs' directory. Inside the docs directory, you can create files using the .mdx extension, and docs.page will generate your documentation site.</p>
</li>
<li><p>Customizable: docs.page allows you to add your logo, social links, theme, analytics, navigation, and more through a simple configuration file.</p>
</li>
<li><p>Live previews: enables viewing of documentation for any branch, pull request, or specific commit, facilitating real-time collaboration and review.</p>
</li>
<li><p>Hot reload: the Hot Reload feature provides real-time previews of documentation changes while editing Markdown (.mdx) files. This feature enhances the local development workflow, enabling instant updates without manual refreshes or rebuilds.</p>
</li>
<li><p>GitHub bot integration: provides a GitHub bot that automatically generates URLs for pull request documentation previews.</p>
</li>
<li><p>MDX support: you can write documentation in Markdown to utilize MDX, which enables you to use React components, such as tabs, Cards, Tweets, and Steps, directly within your Markdown file.</p>
</li>
<li><p>Search functionality: integrates with DocSearch to offer full-text search capabilities within your documentation.</p>
</li>
<li><p>Responsive resign: ensures that your documentation is accessible and visually appealing across a range of devices and screen sizes.</p>
</li>
<li><p>Dark/light mode: offers theme customization to switch between dark and light modes.</p>
</li>
<li><p>Code block highlighting: provides syntax highlighting and content copying features for code blocks.</p>
</li>
</ul>
<p>Check out the code <a target="_blank" href="https://github.com/officialrajdeepsingh/docs-page-demo">available in my GitHub repository</a>.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents:</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-does-docspage-work">How Does</a> <a target="_blank" href="http://docs.page">docs.page</a> <a class="post-section-overview" href="#heading-how-does-docspage-work">Work?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-enable-live-preview-in-docspage">How to Enable Live Preview in</a> <a target="_blank" href="http://docs.page">docs.page</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-configure-docspage">How to Configure</a> <a target="_blank" href="http://docs.page">docs.page</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-pre-built-components-in-docspage">How to Use Pre-built Components in</a> <a target="_blank" href="http://docs.page">docs.page</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-diagnose-errors-in-docspage">How to Diagnose Errors in</a> <a target="_blank" href="http://docs.page">docs.page</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-frontmatter">How to Use Frontmatter</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-assets-to-your-docs">How to Add Assets to Your Docs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-publish-your-documentation-website">How to Publish Your Documentation Website</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-can-you-live-preview-your-upcoming-changes-to-your-documentation-website">How can you live preview your upcoming changes to your documentation website?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-how-does-docspage-work">How Does docs.page Work?</h2>
<p>You can easily start creating your documentation page using the <a target="_blank" href="https://use.docs.page/cli">docs.page CLI</a>. It helps you set up a local documentation project by running the following command:</p>
<pre><code class="lang-bash">pnpm dlx @docs.page/cli init docs.page
</code></pre>
<p>The command output appears as follows:</p>
<pre><code class="lang-bash">pnpm dlx @docs.page/cli init docs.page
? Are you sure you want to setup and install docs.page <span class="hljs-keyword">in</span> /home/officialrajdeepsingh/medium/docs.page? yes
Files created:
 - docs.json: Configuration file <span class="hljs-keyword">for</span> your documentation site
 - docs/index.mdx: The home page of your documentation site
 - docs/next-steps.mdx: A page to <span class="hljs-built_in">help</span> you get started with docs.page

Initialization complete. To preview your documentation site, vist https://docs.page/preview <span class="hljs-keyword">in</span> your browser.
</code></pre>
<p>After creating your project with the docs.page CLI, your project structure should appear as follows:</p>
<pre><code class="lang-bash">.
├── docs
│   ├── index.mdx
│   └── next-steps.mdx
└── docs.json

2 directories, 3 files
</code></pre>
<p>The <code>docs</code> folder contains the Markdown file for your documentation, and the <code>docs.json</code> file includes the configuration for your website, such as the header, sidebar, logo, theme, and other settings.</p>
<h2 id="heading-how-to-enable-live-preview-in-docspage">How to Enable Live Preview in docs.page</h2>
<p>You can set up live preview of your local documentation in real-time in the browser – but it's a little different: you don't need to run any development commands on your laptop or machine.</p>
<p>To open the live preview of your local documentation, first visit <a target="_blank" href="https://docs.page">https://docs.page</a> and click the <strong>Local Preview</strong> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745671253187/c2e6ce0b-aedb-4e7e-b680-68e590fc4018.png" alt="Live preview your local documentation in real-time directly in the browser." class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Next, select the documentation project on your laptop or machine and click the "<strong>Select Directory</strong>" button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745664832273/fc03e2d5-02c0-4bce-b40c-a2599ef72195.png" alt=" click on the &quot;Select Directory&quot; button and select directory" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>After clicking the "Select Directory" button, a new window will open depending on your operating system. Its UI may appear different. Then you need to select the project.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745664861969/39a31043-22e8-4d6b-a883-19be0a59ca4d.png" alt="Select the Directory" class="image--center mx-auto" width="1165" height="672" loading="lazy"></p>
<p>After selecting the folder, you will see the following alert message in the browser (“Let site view files?”). To view the live preview of your documentation website, click the "View files" button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745664971450/8ec6f635-a2e4-401e-8fa8-cf49c4e06b9a.png" alt="Click on View files button" class="image--center mx-auto" width="1917" height="706" loading="lazy"></p>
<p>Now you can see a local live preview of the documentation website in the browser, and any changes you make locally will instantly reflect in the browser. By default, your documentation website should appear as follows:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745674174727/dd4f1820-ce04-4244-b395-b055bb8d236a.png" alt="Live preview your documentation website in the browser" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Next, you’ll learn about configuring the Logo, Theme, Header, Social Links, Sidebar, SEO, search, and more on your docs. You’ll also learn how to use the pre-built components, Front Matter, and assets on docs.page, and finally, how to deploy your documentation website.</p>
<h2 id="heading-how-to-configure-docspage">How to Configure docs.page</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745834873075/6c9dd17b-20e2-40dc-87a1-39cc27cf9a20.png" alt="Configure docs.page in the docs.json file." class="image--center mx-auto" width="1915" height="1046" loading="lazy"></p>
<p>The <code>docs.json</code> file is the primary file for configuring your documentation. Below is a <a target="_blank" href="https://use.docs.page/configuration">list of all available configuration options</a>, which you can use to modify the logos, theme, analytics, and more on your docs.</p>
<h3 id="heading-properties"><strong>Properties</strong></h3>
<ul>
<li><p>Basic properties</p>
</li>
<li><p>Logo</p>
</li>
<li><p>Theme</p>
</li>
<li><p>Header</p>
</li>
<li><p>Anchors</p>
</li>
<li><p>Social Links</p>
</li>
<li><p>SEO</p>
</li>
<li><p>Variables</p>
</li>
<li><p>Search</p>
</li>
<li><p>Scripts</p>
</li>
<li><p>Content</p>
</li>
<li><p>Tabs</p>
</li>
<li><p>Sidebar</p>
</li>
</ul>
<p>There’s so much you can configure using docs.page – but in this tutorial, we’ll focus on some of the most important options:</p>
<ul>
<li><p><a class="post-section-overview" href="#heading-properties">Properties</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-basic-properties">Basic Properties</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-logo">Logo</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-theme">Theme</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-header">Header</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-social-links">Social Links</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-seo">SEO</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-search">Search</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tabs">Tabs</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-sidebar">Sidebar</a></p>
</li>
</ul>
<h3 id="heading-basic-properties">Basic Properties</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745915741849/802ba7e3-ae6b-4628-856f-0d78aa4e1bfc.png" alt="Basic properties such as name, description and favicon" class="image--center mx-auto" width="1677" height="414" loading="lazy"></p>
<p>docs.page includes basic common properties, such as name, description, and favicon, which is very important for SEO.</p>
<ul>
<li><p>name (string): The name of your project. It appears in the header and is used for things like SEO metadata.</p>
</li>
<li><p>description (string): A summary of your project. This is used in meta tags and social preview images.</p>
</li>
<li><p>favicon (string | Favicon object): Specifies the favicon shown in the browser tab. You can provide either a single string URL or use a Favicon object to define different icons for light and dark modes:</p>
<ul>
<li><p>light (string): URL for the favicon in light mode.</p>
</li>
<li><p>dark (string): URL for the favicon in dark mode.</p>
</li>
</ul>
</li>
</ul>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Docs.page"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Ship documentation, like you ship code"</span>,
  <span class="hljs-attr">"favicon"</span>: <span class="hljs-string">"https://static.invertase.io/assets/docs.page/docs-page-logo.png"</span>,
   # or
  <span class="hljs-attr">"favicon"</span>: {
    <span class="hljs-attr">"light"</span>: <span class="hljs-string">"https://cdn-icons-png.flaticon.com/24/9664/9664027.png"</span>,
    <span class="hljs-attr">"dark"</span>: <span class="hljs-string">"https://cdn-icons-png.flaticon.com/24/9643/9643115.png"</span>
  }
}
</code></pre>
<h3 id="heading-logo">Logo</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745750725706/1f3f2775-91d8-4365-88b4-1a43160d12c3.png" alt="Configure the logo for your documentation" class="image--center mx-auto" width="1866" height="93" loading="lazy"></p>
<p>Now it’s time to configure the logo for your documentation, which will appear in the header and be used for social preview images.</p>
<p>The minimum height of the logo must be 24px. You can provide URLS for both a light and a dark logo. If you only provide a light or dark logo, and it doesn't work, you may experience issues where your logo doesn't appear on the website when toggling the theme.</p>
<p>You can add the logo to the documentation in two ways:</p>
<ul>
<li>First way:</li>
</ul>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"My Docs"</span>,
  <span class="hljs-attr">"logo"</span>: <span class="hljs-string">"https://cdn-icons-png.flaticon.com/24/2702/2702154.png"</span>,
}
</code></pre>
<ul>
<li>Second way:</li>
</ul>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"My Docs"</span>,
  <span class="hljs-attr">"logo"</span>: {
    <span class="hljs-attr">"light"</span>: <span class="hljs-string">"https://cdn-icons-png.flaticon.com/24/2702/2702154.png"</span>,
    <span class="hljs-attr">"dark"</span>: <span class="hljs-string">"https://cdn-icons-png.flaticon.com/24/2702/2702172.png"</span>
  }
}
</code></pre>
<h3 id="heading-theme">Theme</h3>
<p>Configuring the theme in your documentation is easy. If you don’t provide a theme, the default theme will be used in your documentation.</p>
<p>docs.page includes a theme property in docs.json, which holds a Theme object as its value with the properties <code>defaultTheme</code>, <code>primary</code>, <code>primaryLight</code>, <code>backgroundLight</code>, and <code>backgroundDark</code>.</p>
<ul>
<li><p><code>defaultTheme</code>: You can select a theme, dark or light.</p>
</li>
<li><p><code>primary</code>: The primary colour is used for links, buttons, and other interactive elements.</p>
</li>
<li><p><code>primaryLight</code>: The <code>primaryLight</code> colour option is used in light mode. If your primary light option is not specified in the <code>docs.json</code> file, then the primary colour will be used.</p>
</li>
<li><p><code>primaryDark</code>: The <code>primaryDark</code> colour option is used in dark mode. If your <code>primaryDark</code> option is not specified in the <code>docs.json</code> file, then the primary color will be used.</p>
</li>
<li><p><code>backgroundLight</code>: The <code>backgroundLight</code> option is used to specify the background color of your documentation in light mode.</p>
</li>
<li><p><code>backgroundDark</code>: The <code>backgroundDark</code> option is used to specify the background color of your documentation in dark mode.</p>
</li>
</ul>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
  <span class="hljs-attr">"theme"</span>: {
    <span class="hljs-attr">"defaultTheme"</span>: <span class="hljs-string">"dark"</span>,
    <span class="hljs-attr">"primary"</span>: <span class="hljs-string">"#de40eb"</span>,
    <span class="hljs-attr">"primaryLight"</span>: <span class="hljs-string">"#BFA213"</span>,
    <span class="hljs-attr">"backgroundLight"</span>: <span class="hljs-string">"#e0cfff"</span>,
    <span class="hljs-attr">"backgroundDark"</span>: <span class="hljs-string">"#00101f"</span>
  },
}
</code></pre>
<h3 id="heading-header">Header</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745912602721/2430492a-8d58-4f4b-bc4c-e22f8cb7b52f.png" alt="configuration of the header in your documentation" class="image--center mx-auto" width="1901" height="638" loading="lazy"></p>
<p>Configuring the header in your documentation includes the following properties: <code>showName</code>, <code>showThemeToggle</code>, <code>showGitHubCard</code>, and links.</p>
<ul>
<li><p><code>showName</code>: The <code>showName</code> option displays the documentation name next to the logo in the header and defaults it is true.</p>
</li>
<li><p><code>showThemeToggle</code>: The <code>showThemeToggle</code> option displays the theme toggle button in the header (and defaults to true).</p>
</li>
<li><p><code>showGitHubCard</code>: The <code>showGitHubCard</code> option displays the GitHub card in the header and defaults to true.</p>
</li>
<li><p>Links: The links option contains an array of Link objects to display a navigation in the header of your documentation.</p>
</li>
</ul>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
  <span class="hljs-attr">"header"</span>: {
    <span class="hljs-attr">"showName"</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">"showGitHubCard"</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-attr">"links"</span>: [
      {
        <span class="hljs-attr">"title"</span>: <span class="hljs-string">"GitHub"</span>,
        <span class="hljs-attr">"href"</span>: <span class="hljs-string">"https://github.com/officialrajdeepsingh/docs-page-demo"</span>
      },
      {
        <span class="hljs-attr">"title"</span>: <span class="hljs-string">"X"</span>,
        <span class="hljs-attr">"href"</span>: <span class="hljs-string">"https://x.com/Official_R_deep"</span>
      },
      {
        <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Linkedin"</span>,
        <span class="hljs-attr">"href"</span>: <span class="hljs-string">"https://www.linkedin.com/in/officalrajdeepsingh"</span>
      }
    ]
  }
}
</code></pre>
<h3 id="heading-social-links">Social Links</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745910990442/f48a4b34-d20f-4155-b19a-d8af8a202801.png" alt="configuration of the social links in your documentation" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>The social option contains an object of key-value pairs where the key represents the social platform and the value corresponds to the username or ID. Here’s how you can add them:</p>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
  <span class="hljs-attr">"social"</span>: {
    <span class="hljs-attr">"github"</span>: <span class="hljs-string">"officialrajdeepsingh/docs-page-demo"</span>,
    <span class="hljs-attr">"x"</span>: <span class="hljs-string">"@Official_R_deep"</span>,
    <span class="hljs-attr">"linkedin"</span>: <span class="hljs-string">"officalrajdeepsingh"</span>
  }
}
</code></pre>
<h3 id="heading-seo">SEO</h3>
<p>The SEO option configures the SEO settings for your documentation. The noindex option tells search engines not to index your documentation, and it defaults to false.</p>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
  noindex: <span class="hljs-literal">true</span>
}
</code></pre>
<h3 id="heading-search">Search</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745909928336/a85c4f21-712d-4096-be08-15d605668a68.png" alt="configuration of the search in your documentation" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>To enable search functionality on your documentation site, you can integrate Algolia DocSearch by configuring the docsearch object in your <code>docs.json</code> file like this:</p>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
 <span class="hljs-attr">"search"</span>: {
    <span class="hljs-attr">"docsearch"</span>: {
      <span class="hljs-attr">"appId"</span>: <span class="hljs-string">"YOUR_APP_ID"</span>,
      <span class="hljs-attr">"apiKey"</span>: <span class="hljs-string">"YOUR_API_KEY"</span>,
      <span class="hljs-attr">"indexName"</span>: <span class="hljs-string">"YOUR_INDEX_NAME"</span>
    }
  }
}
</code></pre>
<h3 id="heading-tabs">Tabs</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745909877477/9248bfa7-ed16-4d2a-9d2a-6624d4690123.png" alt="configuration of the tab in your documentation" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Tabs are an array of objects displayed at the top of your documentation website.</p>
<h4 id="heading-properties-1">Properties</h4>
<p>Each Tab object includes the following properties:</p>
<ul>
<li><p>id (string, required): A unique identifier for the tab.</p>
</li>
<li><p>title (string, required): The text label displayed on the tab.</p>
</li>
<li><p>href (string, required): The URL to navigate to when the tab is clicked.</p>
</li>
<li><p>locale (string, optional): If set, this tab is displayed only when viewing documentation for the specified locale.</p>
</li>
</ul>
<p>Here’s an example of a couple tabs:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"tabs"</span>: [
    {
      <span class="hljs-attr">"id"</span>: <span class="hljs-string">"root"</span>,
      <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Documentation"</span>,
      <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/"</span>
    },
    {
      <span class="hljs-attr">"id"</span>: <span class="hljs-string">"components"</span>,
      <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Components"</span>,
      <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/components"</span>
    }
  ],
}
</code></pre>
<h3 id="heading-sidebar">Sidebar</h3>
<p>To display the sidebar on your website, you can configure or define it in the <code>docs.json</code> file your documentation, which will appear in the sidebar of your site.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745911338226/f3404a5d-b715-4e26-97c9-ce8169fe8d6b.png" alt="configuration of the sidebar in your documentation" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>Essentially, a sidebar is a list of links that appears on the side of your documentation. You can organize links using groups and pages by providing an array of sidebar objects.</p>
<h4 id="heading-options">Options:</h4>
<ul>
<li><p>pages: The pages option takes a list of page links to display in the sidebar. It accepts the following options:</p>
<ul>
<li><p>title (required): The title of the sidebar item.</p>
</li>
<li><p>href (required): The URL to link to when the sidebar item is clicked.</p>
</li>
<li><p>icon (optional): The icon to display next to the sidebar item.</p>
</li>
</ul>
</li>
</ul>
<ul>
<li><p>group (string): The title of the group under which the sidebar item will be displayed. If not provided, the item will appear at the top level of the sidebar.</p>
</li>
<li><p>href (string): The URL the sidebar item will link to when clicked.</p>
</li>
<li><p>icon (string): The name of the icon to display next to the sidebar item.</p>
</li>
<li><p>tab (string): If set, the sidebar item will only be shown when a specific tab (matching the provided tab ID) is active.</p>
</li>
</ul>
<pre><code class="lang-json"><span class="hljs-comment">// docs.json</span>
{
<span class="hljs-attr">"sidebar"</span>: [
    {
      <span class="hljs-attr">"pages"</span>: [
        {
          <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Overview"</span>,
          <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/"</span>,
          <span class="hljs-attr">"icon"</span>: <span class="hljs-string">"book"</span>
        },
        {
          <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Configuration"</span>,
          <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/configuration"</span>,
          <span class="hljs-attr">"icon"</span>: <span class="hljs-string">"gear"</span>
        }
      ]
    },
    {
      <span class="hljs-attr">"group"</span>: <span class="hljs-string">"Components"</span>,
      <span class="hljs-attr">"icon"</span>: <span class="hljs-string">"grip"</span>,
      <span class="hljs-attr">"pages"</span>: [
        {
          <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Getting Started"</span>,
          <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/components"</span>,
          <span class="hljs-attr">"icon"</span>: <span class="hljs-string">"rocket"</span>
        },
        {
          <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Accordion"</span>,
          <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/components/accordion"</span>,
          <span class="hljs-attr">"icon"</span>: <span class="hljs-string">"square-caret-down"</span>
        },
        {
          <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Callouts"</span>,
          <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/components/callouts"</span>,
          <span class="hljs-attr">"icon"</span>: <span class="hljs-string">"bullhorn"</span>
        },
        {
          <span class="hljs-attr">"title"</span>: <span class="hljs-string">"Cards"</span>,
          <span class="hljs-attr">"href"</span>: <span class="hljs-string">"/components/cards"</span>,
          <span class="hljs-attr">"icon"</span>: <span class="hljs-string">"square-full"</span>
        }
      ]
    }
  ]
}
</code></pre>
<p>If you want to learn more about this, check out the <a target="_blank" href="https://use.docs.page/configuration#sidebar">documentation here</a>.</p>
<h2 id="heading-how-to-use-pre-built-components-in-docspage">How to Use Pre-built Components in docs.page</h2>
<p>docs.page comes with <a target="_blank" href="https://use.docs.page/components">15 pre-built components</a>, so you don't need to import components into your MDX file. You can use them directly in your MDX file.</p>
<p>In the following example, I’m using the Info Callout component directly within the MDX file, without importing it.</p>
<pre><code class="lang-markdown"><span class="hljs-section">// index.mdx
---</span>
title: Welcome to docs.page!
<span class="hljs-section">description: Get started with docs.page
---</span>

Welcome to docs.page! The init command you just ran has created a basic file struture in your project to help you get started.

<span class="hljs-section">## Walkthrough</span>

<span class="hljs-section">### Configuration</span>

In the root of your directory a new <span class="hljs-code">`docs.json`</span> file has been created. This file is used to configure your documentation site. You can customize the name, description, and sidebar, theme, logos and more using this file.


<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Info</span>&gt;</span></span>Here's a basic example of what the file looks like: <span class="xml"><span class="hljs-tag">&lt;/<span class="hljs-name">Info</span>&gt;</span></span>
</code></pre>
<h2 id="heading-how-to-diagnose-errors-in-docspage">How to Diagnose Errors in docs.page</h2>
<p>If you encounter any errors on your documentation website, you can view all the errors by clicking the diagnostics button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745863994785/16ec2f9a-86e6-4808-abdb-73513a54d428.png" alt="diagnosing the error in docs.page" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<h2 id="heading-how-to-use-frontmatter">How to Use Frontmatter</h2>
<p>Front matter is a block of YAML placed at the beginning of a Markdown file, enclosed between triple-dash <code>(---)</code> lines.</p>
<p>Frontmatter is a way to customise the metadata page directly within your Markdown files, and most importantly, frontmatter is used for SEO.</p>
<pre><code class="lang-markdown"><span class="hljs-section"># docs/getting-started.mdx</span>
---
title: Welcome to Awesome Project
<span class="hljs-section">description: Some awesome docs!
---</span>

<span class="hljs-section"># Welcome!</span>
</code></pre>
<p>Below is a list of some of the <a target="_blank" href="https://use.docs.page/frontmatter">important frontmatter properties</a> in docs.page, including their type and default values:</p>
<ul>
<li><p><code>title</code> (string): The page’s title used in metadata, social cards, and displayed as the main heading.</p>
</li>
<li><p><code>description</code> (string): A summary of the page appears in metadata for SEO and link previews.</p>
</li>
<li><p><code>image</code> (string): URL of an asset used in social cards and (if enabled) shown at the top of the page.</p>
</li>
<li><p><code>redirect</code> (string): A URL to forward visitors to. When set, the page’s content is bypassed.</p>
</li>
<li><p><code>showPageTitle</code> (boolean): Toggle whether the page title appears as a heading at the top.</p>
</li>
<li><p><code>showPageImage</code> (boolean): Toggle whether the front-matter image is rendered at the top.</p>
</li>
<li><p><code>noindex</code> (boolean): If true, instructs search engines not to index the page.</p>
</li>
</ul>
<p><a target="_blank" href="https://use.docs.page/frontmatter">Refer to the documentation</a> for more detail and other frontmatter property information.</p>
<h2 id="heading-how-to-add-assets-to-your-docs">How to Add Assets to Your Docs</h2>
<p>You can include assets, such as images and videos, in your documentation. You can add both remote and local assets.</p>
<h3 id="heading-remote-assets">Remote Assets</h3>
<p>To add remote assets to your documentation, you can reference them directly in your markdown files.</p>
<p>For example, to include an image from a URL:</p>
<pre><code class="lang-markdown"><span class="hljs-section"># getting-started.mdx</span>
---
title: Welcome to get started
<span class="hljs-section">description: Some awesome docs!
---</span>

<span class="hljs-section"># Welcome!</span>

![<span class="hljs-string">Natural</span>](<span class="hljs-link">https://cdn.pixabay.com/photo/2023/04/19/19/11/lake-7938396_960_720.jpg</span>)
</code></pre>
<h3 id="heading-local-assets">Local Assets</h3>
<p>To use local assets in your documentation, create an <code>assets</code> folder inside the <code>docs/</code> directory. Then, add images and videos to the assets folder and reference them in your Markdown files.</p>
<p>Check out the following to better understand:</p>
<pre><code class="lang-bash">docs/
  assets/
    natural.png
  index.mdx
</code></pre>
<p>Within your markdown file, you can reference the image using a relative path:</p>
<pre><code class="lang-markdown">![<span class="hljs-string">Description</span>](<span class="hljs-link">/assets/natural.png</span>)
</code></pre>
<h3 id="heading-different-between-local-vs-remote-assets">Different between Local vs Remote Assets</h3>
<p>Local assets (PNG, JPG, PDF, and so on) are files stored within your project's public folder, while remote assets are files hosted on an external server. You can access your local assets using your domain URL.</p>
<pre><code class="lang-markdown">![<span class="hljs-string">Natural</span>](<span class="hljs-link">./assets/logo.png</span>)
</code></pre>
<p>On the other hand, remote assets are stored on a different server (image hosting), as I mentioned. You can access remote assets with a full URL.</p>
<p>The best examples of remote assets include images from Unsplash, Pixabay, and Pexels that can be used directly in your MDX file.</p>
<pre><code class="lang-markdown">![<span class="hljs-string">Natural</span>](<span class="hljs-link">https://images.unsplash.com/photo-1728044849236-5e8a061e1895</span>)
</code></pre>
<p>You can use remote and local assets based on your requirements – both have advantages and disadvantages. With remote assets, you can add an image directly in your mdx file. When using local assets, you add an image to the public folder and then reference it in your mdx file.</p>
<h2 id="heading-how-to-publish-your-documentation-website">How to Publish Your Documentation Website</h2>
<p>With docs.page, you can easily publish your documentation website. No configuration is required – once your documentation website is ready, you can just push your local code to a GitHub repository.</p>
<p>You can now access your documentation website immediately via the docs.page domain.</p>
<p>For example, if your GitHub repository is officialrajdeepsingh/docs-page-demo, your documentation will be available at <a target="_blank" href="https://docs.page/officialrajdeepsingh/docs-page-demo">https://docs.page/officialrajdeepsingh/docs-page-demo</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745849817613/c1b7b095-121d-4b64-bd7b-a834ca87f8b5.png" alt="publish your documentation website" class="image--center mx-auto" width="1920" height="1048" loading="lazy"></p>
<h2 id="heading-how-to-live-preview-upcoming-changes-to-your-docs-website">How to Live Preview Upcoming Changes to Your Docs Website</h2>
<p>You can view previews of upcoming changes to your documentation before going public. As your documentation website grows, use the <a target="_blank" href="https://github.com/apps/docs-page">docs.page Github app</a> – any pull request you create in your Github repository automatically generates a unique live preview URL.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746278906018/0a299083-ff0a-4aea-a94e-95f94741e9af.png" alt="0a299083-ff0a-4aea-a94e-95f94741e9af" class="image--center mx-auto" width="1920" height="961" loading="lazy"></p>
<p>To configure the docs page of the GitHub application in your repository, follow these steps:</p>
<ol>
<li><p>Go to <a target="_blank" href="https://github.com/apps/docs-page">https://github.com/apps/docs-page</a></p>
</li>
<li><p>Click on the install button.</p>
</li>
<li><p>Select the GitHub account</p>
</li>
<li><p>Select All and single repository.</p>
</li>
<li><p>Click on the install button</p>
</li>
<li><p>Next, enter the password and OTP.</p>
</li>
<li><p>Now if your application is successful, install it in your repository.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746280111575/06d449cd-917a-4908-8d5e-db90cffd3c0f.gif" alt="Creates live previews in your github repository" class="image--center mx-auto" width="800" height="401" loading="lazy"></p>
<p>Whenever you or another developer create a pull request in your repository, the docs page application creates live previews for you.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>docs.page is a free, open-source project that allows you to create instant, fast, and beautiful documentation without requiring any configuration.</p>
<p>I think docs.page offers the best solution for documentation. You can easily set up and deploy your documentation website with the help of docs.page cloud service.</p>
<p>For now, it’s completely free to deploy a documentation website with a <a target="_blank" href="http://docs.page">docs.page</a>, and I hope it stays that way.</p>
<p>If <a target="_blank" href="http://docs.page">docs.page</a> ever decides to charge for their services, that could be troublesome. Hopefully, in that case, they’ll provide a clear guide on how to deploy your website on another cloud platform.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Production-Ready DevOps Pipeline with Free Tools ]]>
                </title>
                <description>
                    <![CDATA[ A few months ago, I dove into DevOps, expecting it to be an expensive journey requiring costly tools and infrastructure. But I discovered you can build professional-grade pipelines using entirely free resources. If DevOps feels out of reach because y... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-production-ready-devops-pipeline-with-free-tools/</link>
                <guid isPermaLink="false">680fe1e69418a1165cb184a2</guid>
                
                    <category>
                        <![CDATA[ Devops ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Devops articles ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Kubernetes ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Terraform ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ YAML ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Opaluwa Emidowojo ]]>
                </dc:creator>
                <pubDate>Mon, 28 Apr 2025 20:15:34 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745864420670/f36eb4a7-a24e-4d6e-859f-db7249ae0da0.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A few months ago, I dove into DevOps, expecting it to be an expensive journey requiring costly tools and infrastructure. But I discovered you can build professional-grade pipelines using entirely free resources.</p>
<p>If DevOps feels out of reach because you’re also concerned about the cost, don't worry. I’ll guide you step-by-step through creating a production-ready pipeline without spending a dime. Let's get started!</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#introduction">Introduction</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-your-source-control-and-project-structure">How to Set Up Your Source Control and Project Structure</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-your-ci-pipeline-with-github-actions">How to Build Your CI Pipeline with GitHub Actions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-optimize-docker-builds-for-ci">How to Optimize Docker Builds for CI</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-infrastructure-as-code-using-terraform-and-free-cloud-providers">Infrastructure as Code Using Terraform and Free Cloud Providers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-container-orchestration-on-minimal-resources">How to Set Up Container Orchestration on Minimal Resources</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-free-deployment-pipeline">How to Create a Free Deployment Pipeline</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-a-comprehensive-monitoring-system">How to Build a Comprehensive Monitoring System</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-implement-security-testing-and-scanning">How to Implement Security Testing and Scanning</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-performance-optimization-and-scaling">Performance Optimization and Scaling</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-complete-cicd-pipeline-example">Putting it All Together</a></p>
</li>
<li><p><a class="post-section-overview" href="#conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-prerequisites">🛠 Prerequisites</h2>
<ul>
<li><p><strong>Basic Git knowledge</strong>: Cloning repos, creating branches, committing code, and creating PRs</p>
</li>
<li><p><strong>Familiarity with command line</strong>: For Docker, Terraform, and Kubernetes</p>
</li>
<li><p><strong>Basic understanding of CI/CD</strong>: Continuous integration/delivery concepts and pipelines</p>
</li>
</ul>
<h3 id="heading-accounts-needed">Accounts needed:</h3>
<ul>
<li><p>GitHub account</p>
</li>
<li><p>At least one cloud provider: AWS Free Tier (recommended), Oracle Cloud Free Tier, or Google Cloud/Azure with free credits</p>
</li>
<li><p>Terraform Cloud (free tier) for infrastructure state management</p>
</li>
<li><p>Grafana Cloud (free tier) for monitoring</p>
</li>
<li><p>UptimeRobot (free tier) for external availability checks</p>
</li>
</ul>
<h3 id="heading-tools-to-install-locally">Tools to Install Locally</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Tool</strong></td><td><strong>Purpose</strong></td><td><strong>Installation Link</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Git</td><td>Version control</td><td><a target="_blank" href="https://git-scm.com/downloads"><strong>Install Git</strong></a></td></tr>
<tr>
<td>Docker</td><td>Containerization</td><td><a target="_blank" href="https://docs.docker.com/get-docker/"><strong>Install Docker</strong></a></td></tr>
<tr>
<td>Node.js &amp; npm</td><td>Sample app &amp; builds</td><td><a target="_blank" href="https://nodejs.org/"><strong>Install Node.js</strong></a></td></tr>
<tr>
<td>Terraform</td><td>Infrastructure as Code</td><td><a target="_blank" href="https://www.terraform.io/downloads"><strong>Install Terraform</strong></a></td></tr>
<tr>
<td>kubectl</td><td>Kubernetes CLI</td><td><a target="_blank" href="https://kubernetes.io/docs/tasks/tools/"><strong>Install kubectl</strong></a></td></tr>
<tr>
<td>k3d</td><td>Lightweight Kubernetes</td><td><a target="_blank" href="https://k3d.io/"><strong>Install k3d</strong></a></td></tr>
<tr>
<td>Trivy</td><td>Container security scanning</td><td><a target="_blank" href="https://aquasecurity.github.io/trivy/v0.18.3/"><strong>Install Trivy</strong></a></td></tr>
<tr>
<td>OWASP ZAP</td><td>Web security scanning</td><td><a target="_blank" href="https://www.zaproxy.org/download/"><strong>Install ZAP</strong></a></td></tr>
</tbody>
</table>
</div><p><strong>Optional but Helpful:</strong></p>
<ul>
<li><p><a target="_blank" href="https://code.visualstudio.com/"><strong>VS Code</strong></a> or any good code editor</p>
</li>
<li><p>Postman for testing APIs</p>
</li>
<li><p>Understanding of YAML and Dockerfiles</p>
</li>
</ul>
<h2 id="heading-introduction">Introduction</h2>
<p>When people hear "DevOps," they often picture complex enterprise systems powered by pricey tools and premium cloud services. But the truth is, you don't actually need a massive budget to build a solid, professional-grade DevOps pipeline. The foundations of good DevOps – automation, consistency, security, and visibility – can be built entirely with free tools.</p>
<p>In this guide, you will learn how to build a production-ready DevOps pipeline using zero-cost resources. We will use a simple CRUD (Create, Read, Update, Delete) app with frontend, backend API, and database as our example project to demonstrate every step of the process.</p>
<h2 id="heading-how-to-set-up-your-source-control-and-project-structure">How to Set Up Your Source Control and Project Structure</h2>
<h3 id="heading-1-create-a-well-structured-repository">1. Create a Well-Structured Repository</h3>
<p>A clean repo is the foundation of your pipeline. We will set up:</p>
<ul>
<li><p>Separate folders for <code>frontend</code>, <code>backend</code>, and <code>infrastructure</code></p>
</li>
<li><p>A <code>.github</code> folder to hold workflow configurations</p>
</li>
<li><p>Clear naming conventions and a well-written <code>README.md</code></p>
</li>
</ul>
<p>🛠 <strong>Tip</strong>: Use semantic commit messages and consider adopting <a target="_blank" href="https://www.conventionalcommits.org/"><strong>Conventional Commits</strong></a> for clarity in versioning and changelogs.</p>
<h3 id="heading-2-set-up-branch-protection-without-paid-features">2. Set Up Branch Protection Without Paid Features</h3>
<p>While GitHub's more advanced rules require Pro, you can still:</p>
<ul>
<li><p>Require pull requests before merging</p>
</li>
<li><p>Enable status checks to prevent broken code from landing in <code>main</code></p>
</li>
<li><p>Enforce linear history for cleaner version control</p>
</li>
</ul>
<p>💡 This makes your project safer and more collaborative, without needing GitHub Enterprise.</p>
<h3 id="heading-3-implement-pr-templates-and-automated-checks">3. Implement PR Templates and Automated Checks</h3>
<p>Make your reviews smoother:</p>
<ul>
<li><p>Add a <code>PULL_REQUEST_TEMPLATE.md</code> to guide contributors</p>
</li>
<li><p>Use GitHub Actions (which we'll set up in the next part) for linting, tests, and formatting checks</p>
</li>
</ul>
<p>✨ These tiny improvements add polish and professionalism.</p>
<h3 id="heading-4-configure-github-issue-templates-and-project-boards">4. Configure GitHub Issue Templates and Project Boards</h3>
<p>Even solo developers benefit from issue tracking:</p>
<ul>
<li><p>Add issue templates for bugs and features</p>
</li>
<li><p>Use GitHub Projects to manage work with a Kanban board, all free and native to GitHub</p>
</li>
</ul>
<p>📌 <strong>Bonus</strong>: This setup lays the groundwork for GitOps practices later on.</p>
<h3 id="heading-5-advanced-technique-set-up-custom-validation-scripts-as-pre-commit-hooks">5. Advanced Technique: Set Up Custom Validation Scripts as Pre-Commit Hooks</h3>
<p>Before code ever hits GitHub, you can catch issues locally with Git hooks. Using a tool like <a target="_blank" href="https://typicode.github.io/husky/"><strong>Husky</strong></a> or <a target="_blank" href="https://pre-commit.com/"><strong>pre-commit</strong></a>, you can:</p>
<ul>
<li><p>Lint code before it's committed</p>
</li>
<li><p>Run tests or formatters automatically</p>
</li>
<li><p>Prevent secrets from being accidentally committed</p>
</li>
</ul>
<pre><code class="lang-json"><span class="hljs-comment">// Initialize Husky and install needed dependencies</span>
<span class="hljs-comment">// Then add a pre-commit hook that runs tests before allowing the commit</span>
npx husky-init &amp;&amp; npm install
npx husky add .husky/pre-commit <span class="hljs-string">"npm test"</span>
</code></pre>
<h3 id="heading-6-sample-crud-app-setup"><strong>6. Sample CRUD App Setup:</strong></h3>
<p>Our CRUD app manages users (create, read, update, delete). Below is the minimal code with comments to explain each part:</p>
<p><strong>Backend</strong> <code>(backend/)</code>:</p>
<pre><code class="lang-json"><span class="hljs-comment">// backend/package.json</span>
{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"crud-backend"</span>, <span class="hljs-comment">// Name of the backend project</span>
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>, <span class="hljs-comment">// Version for tracking changes</span>
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"node index.js"</span>, <span class="hljs-comment">// Runs the server</span>
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"echo 'Add tests here'"</span>, <span class="hljs-comment">// Placeholder for tests (update with Jest later)</span>
    <span class="hljs-attr">"lint"</span>: <span class="hljs-string">"eslint ."</span> <span class="hljs-comment">// Checks code style with ESLint</span>
  },
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"express"</span>: <span class="hljs-string">"^4.17.1"</span>, <span class="hljs-comment">// Web framework for API endpoints</span>
    <span class="hljs-attr">"pg"</span>: <span class="hljs-string">"^8.7.3"</span> <span class="hljs-comment">// PostgreSQL client to connect to the database</span>
  },
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"eslint"</span>: <span class="hljs-string">"^8.0.0"</span> <span class="hljs-comment">// Linting tool for code quality</span>
  }
}
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-comment">// backend/index.js</span>
<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>); <span class="hljs-comment">// Import Express for building the API</span>
<span class="hljs-keyword">const</span> { Pool } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'pg'</span>); <span class="hljs-comment">// Import PostgreSQL client</span>
<span class="hljs-keyword">const</span> app = express(); <span class="hljs-comment">// Create an Express app</span>
app.use(express.json()); <span class="hljs-comment">// Parse JSON request bodies</span>

<span class="hljs-comment">// Connect to PostgreSQL using DATABASE_URL from environment variables</span>
<span class="hljs-keyword">const</span> pool = <span class="hljs-keyword">new</span> Pool({ <span class="hljs-attr">connectionString</span>: process.env.DATABASE_URL });

<span class="hljs-comment">// Health check endpoint for Kubernetes probes and monitoring</span>
app.get(<span class="hljs-string">'/healthz'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> res.json({ <span class="hljs-attr">status</span>: <span class="hljs-string">'ok'</span> }));

<span class="hljs-comment">// Get all users from the database</span>
app.get(<span class="hljs-string">'/users'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { rows } = <span class="hljs-keyword">await</span> pool.query(<span class="hljs-string">'SELECT * FROM users'</span>); <span class="hljs-comment">// Query the users table</span>
  res.json(rows); <span class="hljs-comment">// Send users as JSON</span>
});

<span class="hljs-comment">// Add a new user to the database</span>
app.post(<span class="hljs-string">'/users'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { name } = req.body; <span class="hljs-comment">// Get name from request body</span>
  <span class="hljs-comment">// Insert user and return the new record</span>
  <span class="hljs-keyword">const</span> { rows } = <span class="hljs-keyword">await</span> pool.query(<span class="hljs-string">'INSERT INTO users(name) VALUES($1) RETURNING *'</span>, [name]);
  res.json(rows[<span class="hljs-number">0</span>]); <span class="hljs-comment">// Send the new user as JSON</span>
});

<span class="hljs-comment">// Start the server on port 3000</span>
app.listen(<span class="hljs-number">3000</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Backend running on port 3000'</span>));
</code></pre>
<p><strong>Frontend</strong> <code>(frontend/)</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// frontend/package.json</span>
{
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"crud-frontend"</span>, <span class="hljs-comment">// Name of the frontend project</span>
  <span class="hljs-string">"version"</span>: <span class="hljs-string">"1.0.0"</span>, <span class="hljs-comment">// Version for tracking changes</span>
  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"start"</span>: <span class="hljs-string">"react-scripts start"</span>, <span class="hljs-comment">// Runs the dev server</span>
    <span class="hljs-string">"build"</span>: <span class="hljs-string">"react-scripts build"</span>, <span class="hljs-comment">// Builds for production</span>
    <span class="hljs-string">"test"</span>: <span class="hljs-string">"react-scripts test"</span>, <span class="hljs-comment">// Runs tests (placeholder for Jest)</span>
    <span class="hljs-string">"lint"</span>: <span class="hljs-string">"eslint ."</span> <span class="hljs-comment">// Checks code style with ESLint</span>
  },
  <span class="hljs-string">"dependencies"</span>: {
    <span class="hljs-string">"react"</span>: <span class="hljs-string">"^17.0.2"</span>, <span class="hljs-comment">// Core React library</span>
    <span class="hljs-string">"react-dom"</span>: <span class="hljs-string">"^17.0.2"</span>, <span class="hljs-comment">// Renders React to the DOM</span>
    <span class="hljs-string">"react-scripts"</span>: <span class="hljs-string">"^4.0.3"</span>, <span class="hljs-comment">// Scripts for React development</span>
    <span class="hljs-string">"axios"</span>: <span class="hljs-string">"^0.24.0"</span> <span class="hljs-comment">// HTTP client for API calls</span>
  },
  <span class="hljs-string">"devDependencies"</span>: {
    <span class="hljs-string">"eslint"</span>: <span class="hljs-string">"^8.0.0"</span> <span class="hljs-comment">// Linting tool for code quality</span>
  }
}
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-comment">// frontend/src/App.js</span>
<span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>; <span class="hljs-comment">// Import React and hooks</span>
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>; <span class="hljs-comment">// Import Axios for API requests</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// State for storing users fetched from the backend</span>
  <span class="hljs-keyword">const</span> [users, setUsers] = useState([]);
  <span class="hljs-comment">// State for the input field to add a new user</span>
  <span class="hljs-keyword">const</span> [name, setName] = useState(<span class="hljs-string">''</span>);

  <span class="hljs-comment">// Fetch users when the component mounts</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    axios.get(<span class="hljs-string">'http://localhost:3000/users'</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> setUsers(res.data));
  }, []); <span class="hljs-comment">// Empty array means run once on mount</span>

  <span class="hljs-comment">// Add a new user via the API</span>
  <span class="hljs-keyword">const</span> addUser = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.post(<span class="hljs-string">'http://localhost:3000/users'</span>, { name }); <span class="hljs-comment">// Post new user</span>
    setUsers([...users, res.data]); <span class="hljs-comment">// Update users list</span>
    setName(<span class="hljs-string">''</span>); <span class="hljs-comment">// Clear input field</span>
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Users<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      {/* Input for new user name */}
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{name}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{e</span> =&gt;</span> setName(e.target.value)} /&gt;
      {/* Button to add user */}
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{addUser}</span>&gt;</span>Add User<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      {/* List all users */}
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>{users.map(user =&gt; <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>)}<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App; <span class="hljs-comment">// Export the component</span>
</code></pre>
<p><strong>Database Setup</strong>:</p>
<pre><code class="lang-pgsql"><span class="hljs-comment">-- infra/db.sql</span>
<span class="hljs-comment">-- Create a table to store users</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> users (
  id <span class="hljs-type">SERIAL</span> <span class="hljs-keyword">PRIMARY KEY</span>, <span class="hljs-comment">-- Auto-incrementing ID</span>
  <span class="hljs-type">name</span> <span class="hljs-type">VARCHAR</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-comment">-- User name, required</span>
);
</code></pre>
<pre><code class="lang-javascript">crud-app/
├── backend/
│   ├── package.json
│   └── index.js
├── frontend/
│   ├── package.json
│   └── src/App.js
├── infra/
│   └── db.sql
├── .github/
│   └── workflows/
└── README.md
</code></pre>
<p>This app provides a <code>/users</code> endpoint (GET/POST) and a frontend to list/add users, stored in PostgreSQL. The <code>/healthz</code> endpoint supports monitoring. Save this code in your repo to follow the pipeline steps.</p>
<h2 id="heading-how-to-build-your-ci-pipeline-with-github-actions">How to Build Your CI Pipeline with GitHub Actions</h2>
<h3 id="heading-1-set-up-your-first-github-actions-workflow">1. Set Up Your First GitHub Actions Workflow</h3>
<p>First, let’s create a basic workflow that automatically builds, tests, and lints your app every time you push code or open a pull request. This ensures your app stays healthy and any issues are caught early.</p>
<p>Create a file at <code>.github/workflows/ci.yml</code> and add the following:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># CI workflow to build, test, and lint the CRUD app on push or pull request</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">CI</span> <span class="hljs-string">Pipeline</span>
<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">main</span>] <span class="hljs-comment"># Trigger on pushes to main branch</span>
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">main</span>] <span class="hljs-comment"># Trigger on PRs to main branch</span>
<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span> <span class="hljs-comment"># Use GitHub's free Linux runner</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span> <span class="hljs-comment"># Check out the repository code</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-comment"># Install Node.js environment</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-string">'18'</span> <span class="hljs-comment"># Use Node.js 18 for consistency</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Cache</span> <span class="hljs-string">dependencies</span> <span class="hljs-comment"># Cache node_modules to speed up builds</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/cache@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">~/.npm</span> <span class="hljs-comment"># Cache npm’s global cache</span>
          <span class="hljs-attr">key:</span> <span class="hljs-string">${{</span> <span class="hljs-string">runner.os</span> <span class="hljs-string">}}-node-${{</span> <span class="hljs-string">hashFiles('**/package-lock.json')</span> <span class="hljs-string">}}</span> <span class="hljs-comment"># Key based on OS and package-lock.json</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span> <span class="hljs-comment"># Install dependencies reliably using package-lock.json</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">test</span> <span class="hljs-comment"># Run tests defined in package.json</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">lint</span> <span class="hljs-comment"># Run ESLint to ensure code quality</span>
</code></pre>
<p>This workflow automatically runs on every push and pull request to the <code>main</code> branch. It installs dependencies, runs tests, and performs code linting, with dependency caching to make builds faster over time.</p>
<p><strong>Common Issues and Fixes</strong>:</p>
<ul>
<li><p><strong>“Secret not found”</strong>: Ensure <code>AWS_ACCESS_KEY_ID</code> is in repository secrets (Settings → Secrets).</p>
</li>
<li><p><strong>Tests fail</strong>: Check <code>test/users.test.js</code> for database connectivity.</p>
</li>
</ul>
<h4 id="heading-understanding-github-actions-free-tier-limits">Understanding GitHub Actions' Free Tier Limits</h4>
<p>Before building more workflows, it is important to know what GitHub offers for free.</p>
<p>If you are working on private repositories, you get 2,000 free minutes per month. For public repositories, you get unlimited minutes.</p>
<p>To avoid hitting limits quickly:</p>
<ul>
<li><p>Cache your dependencies to cut down install times.</p>
</li>
<li><p>Only trigger workflows on meaningful branches (like <code>main</code> or <code>release</code>).</p>
</li>
<li><p>Skip unnecessary steps when you can.</p>
</li>
</ul>
<h3 id="heading-2-creating-a-multi-stage-build-pipeline">2. Creating a Multi-Stage Build Pipeline</h3>
<p>As your app grows, it is better to split your CI pipeline into clear stages like <strong>install</strong>, <strong>test</strong>, and <strong>lint</strong>. This structure makes workflows easier to maintain and speeds things up, because some jobs can run in parallel.</p>
<p>Here’s how you can split the work into multiple jobs for better clarity:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">install:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span>  <span class="hljs-comment"># Clean install of dependencies</span>

  <span class="hljs-attr">test:</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">install</span>  <span class="hljs-comment"># This job depends on the install job finishing</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">test</span>  <span class="hljs-comment"># Run test suite</span>

  <span class="hljs-attr">lint:</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">install</span>  <span class="hljs-comment"># This job also depends on install but runs in parallel with test</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">lint</span>  <span class="hljs-comment"># Run linting checks</span>
</code></pre>
<p>By breaking the pipeline into stages, you can quickly spot which step fails, and your test and lint jobs can run at the same time after dependencies are installed.</p>
<h3 id="heading-3-implement-matrix-builds-for-cross-environment-testing">3. Implement Matrix Builds for Cross-Environment Testing</h3>
<p>When you want your app to work across different Node.js versions or databases, matrix builds are your best bet. They let you test across multiple environments in parallel, without duplicating code.</p>
<p>Here’s how you can set up a matrix strategy, to test across multiple environments simultaneously:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">test:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">strategy:</span>
      <span class="hljs-attr">matrix:</span>
        <span class="hljs-attr">node-version:</span> [<span class="hljs-number">14.</span><span class="hljs-string">x</span>, <span class="hljs-number">16.</span><span class="hljs-string">x</span>, <span class="hljs-number">18.</span><span class="hljs-string">x</span>]  <span class="hljs-comment"># Test on multiple Node versions</span>
        <span class="hljs-attr">database:</span> [<span class="hljs-string">postgres</span>, <span class="hljs-string">mysql</span>]        <span class="hljs-comment"># Test against different databases</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</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">Use</span> <span class="hljs-string">Node.js</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.node-version</span> <span class="hljs-string">}}</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-string">${{</span> <span class="hljs-string">matrix.node-version</span> <span class="hljs-string">}}</span>
      <span class="hljs-bullet">-</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">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">test</span>  <span class="hljs-comment"># This will run 6 different test combinations (3 Node versions × 2 databases)</span>
</code></pre>
<p>Matrix builds save time and help you catch environment-specific bugs early.</p>
<h3 id="heading-4-optimize-workflow-with-dependency-caching">4. Optimize Workflow with Dependency Caching</h3>
<p>Every second counts in CI. Dependency caching can help save minutes in your workflow by reusing previously installed packages instead of reinstalling them from scratch every time.</p>
<p>Here’s how to set up smart caching to speed up your builds:</p>
<pre><code class="lang-yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Cache</span> <span class="hljs-string">node</span> <span class="hljs-string">modules</span>
  <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/cache@v3</span>
  <span class="hljs-attr">with:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">|</span>  <span class="hljs-comment"># Cache both global npm cache and local node_modules</span>
      <span class="hljs-string">~/.npm</span>
      <span class="hljs-string">node_modules</span>
    <span class="hljs-attr">key:</span> <span class="hljs-string">${{</span> <span class="hljs-string">runner.os</span> <span class="hljs-string">}}-node-${{</span> <span class="hljs-string">hashFiles('**/package-lock.json')</span> <span class="hljs-string">}}</span>  <span class="hljs-comment"># Cache key based on OS and dependencies</span>
    <span class="hljs-attr">restore-keys:</span> <span class="hljs-string">|</span>  <span class="hljs-comment"># Fallback keys if exact match isn't found</span>
      <span class="hljs-string">${{</span> <span class="hljs-string">runner.os</span> <span class="hljs-string">}}-node-</span>
</code></pre>
<p>This cache setup checks if your dependencies have changed. If not, it restores the cache, making builds significantly faster.</p>
<h2 id="heading-how-to-optimize-docker-builds-for-ci">How to Optimize Docker Builds for CI</h2>
<p>When you're building Docker images in CI, build time can quickly become a bottleneck. Especially if your images are large. Optimizing your Docker builds makes your pipelines much faster, saves bandwidth, and produces smaller, more efficient images ready for deployment.</p>
<p>In this section, I’ll walk through creating a basic Dockerfile, using multi-stage builds, caching layers, and enabling BuildKit for even faster builds.</p>
<h3 id="heading-1-create-a-baseline-dockerfile">1. Create a Baseline Dockerfile</h3>
<p>First, start with a simple Dockerfile that installs your app’s dependencies and runs it. This is what you’ll be optimizing later.</p>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># Simple Dockerfile for a Node.js application</span>
<span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18</span>-alpine  <span class="hljs-comment"># Use Alpine for a smaller base image</span>
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app         <span class="hljs-comment"># Set working directory</span></span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .             <span class="hljs-comment"># Copy all files to container</span></span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm ci           <span class="hljs-comment"># Install dependencies (clean install)</span></span>
<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"npm"</span>, <span class="hljs-string">"start"</span>] <span class="hljs-comment"># Start the application</span></span>
</code></pre>
<p>Using an Alpine-based Node.js image helps keep your image small from the start.</p>
<h3 id="heading-2-multi-stage-docker-builds">2. Multi-Stage Docker Builds</h3>
<p>Next, let's separate the build process from the production image. Multi-stage builds let you compile or build your app in one stage and only copy over the final product to a clean, smaller image. This keeps production images lean:</p>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># Stage 1: Build the application</span>
<span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18</span>-alpine AS builder
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-keyword">COPY</span><span class="bash"> package*.json ./  <span class="hljs-comment"># Copy package files first for better caching</span></span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm ci             <span class="hljs-comment"># Install all dependencies</span></span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .               <span class="hljs-comment"># Then copy source code</span></span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm run build      <span class="hljs-comment"># Build the application</span></span>

<span class="hljs-comment"># Stage 2: Production image with minimal footprint</span>
<span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18</span>-alpine
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-comment"># Only copy built assets and production dependencies</span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=builder /app/dist ./dist</span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=builder /app/package*.json ./</span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm ci --production  <span class="hljs-comment"># Install only production dependencies</span></span>
<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"node"</span>, <span class="hljs-string">"dist/server.js"</span>]  <span class="hljs-comment"># Run the built application</span></span>
</code></pre>
<p>This approach keeps your production images lightweight and secure by excluding unnecessary build tools and dev dependencies.</p>
<h3 id="heading-3-optimizing-layer-caching">3. Optimizing Layer Caching</h3>
<p>For even faster builds, order your <code>Dockerfile</code> instructions to maximize layer caching. Copy and install dependencies <em>before</em> copying your full source code.</p>
<p>This way, Docker reuses the cached npm install step if your dependencies haven't changed, even if you edit your app's code:</p>
<ul>
<li><p>First: <code>COPY package*.json ./</code></p>
</li>
<li><p>Then: <code>RUN npm ci</code></p>
</li>
<li><p>Finally: <code>COPY . .</code></p>
</li>
</ul>
<h3 id="heading-4-enable-buildkit-for-faster-builds">4. Enable BuildKit for Faster Builds</h3>
<p>Docker BuildKit is a newer build engine that enables features like better caching, parallel build steps, and overall faster builds.</p>
<p>To enable BuildKit during your CI, run:</p>
<pre><code class="lang-dockerfile">- name: Build Docker image
  <span class="hljs-keyword">run</span><span class="bash">: |</span>
    <span class="hljs-comment"># Enable BuildKit for parallel and more efficient builds</span>
    DOCKER_BUILDKIT=<span class="hljs-number">1</span> docker build -t myapp:latest .
</code></pre>
<p>Turning on BuildKit can significantly speed up complex Docker builds and is highly recommended for all CI pipelines.</p>
<h2 id="heading-infrastructure-as-code-using-terraform-and-free-cloud-providers">Infrastructure as Code Using Terraform and Free Cloud Providers</h2>
<h3 id="heading-why-infrastructure-as-code-iac-matters">Why Infrastructure as Code (IaC) Matters</h3>
<p>When you manage infrastructure manually – that is, clicking around cloud dashboards or setting things up by hand – it’s easy to lose track of what you did and how to repeat it.</p>
<p>Infrastructure as Code (IaC) solves this by letting you define your infrastructure with code, version it just like application code, and track every change over time. This makes your setups easy to replicate across environments (development, staging, production), ensures changes are declarative and auditable, and reduces human error.</p>
<p>Whether you are spinning up a single server or scaling a complex system, IaC lays the foundation for professional-grade infrastructure from day one, letting you automate, document, and grow your environment systematically.</p>
<h3 id="heading-how-to-provision-infrastructure-with-terraform">How to Provision Infrastructure with Terraform</h3>
<h4 id="heading-initialize-a-terraform-project">Initialize a Terraform Project</h4>
<p>First, define the providers and versions you need. Here, we’re using Render’s free cloud hosting service:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Define required providers and versions</span>
<span class="hljs-string">terraform</span> {
  <span class="hljs-string">required_providers</span> {
    <span class="hljs-string">render</span> <span class="hljs-string">=</span> {
      <span class="hljs-string">source</span>  <span class="hljs-string">=</span> <span class="hljs-string">"renderinc/render"</span>  <span class="hljs-comment"># Using Render's free tier</span>
      <span class="hljs-string">version</span> <span class="hljs-string">=</span> <span class="hljs-string">"0.1.0"</span>             <span class="hljs-comment"># Specify provider version for stability</span>
    }
  }
}

<span class="hljs-comment"># Configure the Render provider with authentication</span>
<span class="hljs-string">provider</span> <span class="hljs-string">"render"</span> {
  <span class="hljs-string">api_key</span> <span class="hljs-string">=</span> <span class="hljs-string">var.render_api_key</span>  <span class="hljs-comment"># Store API key as a variable</span>
}
</code></pre>
<p>Then, configure the provider by authenticating with your API key. It is best practice to store secrets like API keys in variables instead of hardcoding them. This setup tells Terraform what platform you’re working with (Render) and how to authenticate to manage resources automatically.</p>
<h4 id="heading-provision-a-web-app-on-render">Provision a Web App on Render</h4>
<p>Next, define the infrastructure you want – in this case, a web service hosted on Render:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Define a web service on Render's free tier</span>
<span class="hljs-string">resource</span> <span class="hljs-string">"render_service"</span> <span class="hljs-string">"web_app"</span> {
  <span class="hljs-string">name</span> <span class="hljs-string">=</span> <span class="hljs-string">"ci-demo-app"</span>                                 <span class="hljs-comment"># Service name</span>
  <span class="hljs-string">type</span> <span class="hljs-string">=</span> <span class="hljs-string">"web_service"</span>                                 <span class="hljs-comment"># Type of service</span>
  <span class="hljs-string">repo</span> <span class="hljs-string">=</span> <span class="hljs-string">"https://github.com/YOUR-USERNAME/YOUR-REPO"</span>  <span class="hljs-comment"># Source repo</span>
  <span class="hljs-string">env</span> <span class="hljs-string">=</span> <span class="hljs-string">"docker"</span>                                       <span class="hljs-comment"># Use Docker environment</span>
  <span class="hljs-string">plan</span> <span class="hljs-string">=</span> <span class="hljs-string">"starter"</span>                                     <span class="hljs-comment"># Free tier plan</span>
  <span class="hljs-string">branch</span> <span class="hljs-string">=</span> <span class="hljs-string">"main"</span>                                      <span class="hljs-comment"># Deploy from main branch</span>
  <span class="hljs-string">build_command</span> <span class="hljs-string">=</span> <span class="hljs-string">"docker build -t app ."</span>              <span class="hljs-comment"># Build command</span>
  <span class="hljs-string">start_command</span> <span class="hljs-string">=</span> <span class="hljs-string">"docker run -p 3000:3000 app"</span>        <span class="hljs-comment"># Start command</span>
  <span class="hljs-string">auto_deploy</span> <span class="hljs-string">=</span> <span class="hljs-literal">true</span>                                   <span class="hljs-comment"># Auto-deploy on commits</span>
}
</code></pre>
<p>This resource block describes exactly how your app should be deployed. Whenever you change this file and reapply, Terraform will update the infrastructure to match.</p>
<h4 id="heading-provision-postgresql-for-free">Provision PostgreSQL for Free</h4>
<p>Most applications need a database, but you don't have to pay for one when you're getting started. Platforms like <a target="_blank" href="https://railway.app/">Railway</a> offer free tiers that are perfect for development and small projects.</p>
<p>You can quickly create a free PostgreSQL instance by signing up on the platform and clicking <strong>"Create New Project"</strong>. At the end, you'll get a <code>DATABASE_URL</code> a connection string that your app will use to talk to the database.</p>
<h4 id="heading-connect-app-to-db">Connect App to DB</h4>
<p>In Render (or whatever platform you're using), set an environment variable called <code>DATABASE_URL</code> and paste in the connection string from your PostgreSQL provider. This lets your application securely access the database without hardcoding credentials into your codebase.</p>
<h4 id="heading-make-it-reproducible">Make it Reproducible</h4>
<p>Once everything is defined, use Terraform to create and apply an infrastructure plan:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Create execution plan and save it to a file</span>
<span class="hljs-string">terraform</span> <span class="hljs-string">plan</span> <span class="hljs-string">-out=infra.tfplan</span>
<span class="hljs-comment"># Apply the saved plan exactly as planned</span>
<span class="hljs-string">terraform</span> <span class="hljs-string">apply</span> <span class="hljs-string">infra.tfplan</span>
</code></pre>
<p>Saving the plan to a file (<code>infra.tfplan</code>) ensures you’re applying exactly what you reviewed, so there will be no surprises.</p>
<p><strong>Common Issues and Fixes</strong>:</p>
<ul>
<li><p><strong>Provider not found</strong>: Run <code>terraform init</code>.</p>
</li>
<li><p><strong>API key error</strong>: Check <code>render_api_key</code> in Terraform Cloud variables.</p>
</li>
</ul>
<h2 id="heading-how-to-set-up-container-orchestration-on-minimal-resources">How to Set Up Container Orchestration on Minimal Resources</h2>
<p>When you're working with limited resources like a laptop, a small server, or a lightweight cloud VM, setting up full Kubernetes can be overwhelming. Instead, you can use <strong>K3d</strong>, a lightweight Kubernetes distribution that runs inside Docker containers. Here's how to set up a minimal, efficient cluster for local development or testing.</p>
<h3 id="heading-1-install-k3d-for-local-kubernetes">1. Install K3d for Local Kubernetes</h3>
<p>First, install K3d. It's a super lightweight way to run Kubernetes clusters inside Docker without needing a heavy setup like Minikube.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Download and install K3d - a lightweight K8s distribution</span>
curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash
</code></pre>
<h3 id="heading-2-create-a-lightweight-k3d-cluster">2. Create a Lightweight K3d Cluster</h3>
<p>Once K3d is installed, you can spin up a cluster with minimal nodes to save resources.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a minimal K8s cluster with 1 server and 2 agent nodes</span>
k3d cluster create dev-cluster \
  --servers 1 \                        <span class="hljs-comment"># Single server node to minimize resource usage</span>
  --agents 2 \                         <span class="hljs-comment"># Two worker nodes for pod distribution</span>
  --volume /tmp/k3dvol:/tmp/k3dvol \   <span class="hljs-comment"># Mount local volume for persistence</span>
  --port 8080:80@loadbalancer \        <span class="hljs-comment"># Map port 8080 locally to 80 in the cluster</span>
  --api-port 6443                      <span class="hljs-comment"># Set the API port</span>
</code></pre>
<p>This setup gives you a <strong>tiny but real Kubernetes cluster</strong> that is perfect for experimentation.</p>
<h3 id="heading-3-deploy-with-optimized-kubernetes-manifests">3. Deploy with Optimized Kubernetes Manifests</h3>
<p>Now that your cluster is running, you can deploy your app. It's important to define resource requests and limits carefully so your pods don’t consume too much memory or CPU.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Resource-optimized deployment manifest</span>
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp  <span class="hljs-comment"># Name of the deployment</span>
spec:
  replicas: 1   <span class="hljs-comment"># Single replica to save resources</span>
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
        - name: app
          image: myapp:latest
          resources:
            <span class="hljs-comment"># Set minimal resource requests</span>
            requests:
              memory: <span class="hljs-string">"64Mi"</span>   <span class="hljs-comment"># Request only 64MB memory</span>
              cpu: <span class="hljs-string">"50m"</span>       <span class="hljs-comment"># Request only 5% of a CPU core</span>
            <span class="hljs-comment"># Set reasonable limits</span>
            limits:
              memory: <span class="hljs-string">"128Mi"</span>  <span class="hljs-comment"># Limit to 128MB memory</span>
              cpu: <span class="hljs-string">"100m"</span>      <span class="hljs-comment"># Limit to 10% of a CPU core</span>
</code></pre>
<p>This ensures Kubernetes knows how much to allocate and avoid overloading your lightweight environment.</p>
<h3 id="heading-4-set-up-gitops-with-flux">4. Set up GitOps with Flux</h3>
<p>To manage deployments automatically from your GitHub repository, you can set up GitOps using Flux.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Install Flux CLI</span>
brew install fluxcd/tap/flux

<span class="hljs-comment"># Bootstrap Flux on your cluster connected to your GitHub repository</span>
flux bootstrap github \
  --owner=YOUR_GITHUB_USERNAME \    <span class="hljs-comment"># Your GitHub username</span>
  --repository=YOUR_REPO_NAME \     <span class="hljs-comment"># Repository to store Flux manifests</span>
  --branch=main \                   <span class="hljs-comment"># Branch to use</span>
  --path=clusters/dev-cluster \     <span class="hljs-comment"># Path within repo for cluster configs</span>
  --personal                        <span class="hljs-comment"># Flag for personal account</span>
</code></pre>
<p>Flux watches your repo and applies updates to your cluster, keeping everything declarative and reproducible.</p>
<p><strong>Common Issues and Fixes</strong>:</p>
<ul>
<li><p><strong>Pods crash</strong>: Run <code>kubectl logs pod-name</code> or increase resources.</p>
</li>
<li><p><strong>Flux sync fails</strong>: Check GitHub token permissions.</p>
</li>
</ul>
<h2 id="heading-how-to-create-a-free-deployment-pipeline">How to Create a Free Deployment Pipeline</h2>
<p>Like I said initially, not every project needs expensive infrastructure. If you're just getting started or building side projects, free tiers from cloud providers can cover a lot of ground.</p>
<h3 id="heading-1-understanding-free-tier-limitations">1. Understanding Free Tier Limitations</h3>
<p>Here’s a quick overview of popular cloud free tiers:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Provider</td><td>Free Tier Highlights</td></tr>
</thead>
<tbody>
<tr>
<td>AWS Free Tier</td><td>750 hours/month EC2, 5GB S3, 1M Lambda requests</td></tr>
<tr>
<td>Oracle Cloud Free Tier</td><td>2 always-free compute instances, 30GB storage</td></tr>
<tr>
<td>Google Cloud Free Tier</td><td>1 f1-micro instance, 5GB storage</td></tr>
</tbody>
</table>
</div><p>Knowing these limits helps you stay within budget.</p>
<h3 id="heading-2-set-up-deployment-workflows">2. Set Up Deployment Workflows</h3>
<p>You can automate deployments with GitHub Actions. Here's an example of a deployment workflow to AWS:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># GitHub Action workflow for deploying to AWS</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">AWS</span> <span class="hljs-string">Deployment</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"># Deploy on push to main branch</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">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>  <span class="hljs-comment"># Check out code</span>

      <span class="hljs-comment"># Set up AWS credentials from GitHub secrets</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">AWS</span> <span class="hljs-string">credentials</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">aws-actions/configure-aws-credentials@v1</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">aws-access-key-id:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_ACCESS_KEY_ID</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">aws-secret-access-key:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_SECRET_ACCESS_KEY</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">aws-region:</span> <span class="hljs-string">us-east-1</span>

      <span class="hljs-comment"># Build the Docker image</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">Docker</span> <span class="hljs-string">Image</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">docker</span> <span class="hljs-string">build</span> <span class="hljs-string">-t</span> <span class="hljs-string">myapp</span> <span class="hljs-string">.</span>

      <span class="hljs-comment"># Push the image to AWS ECR</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Push</span> <span class="hljs-string">Docker</span> <span class="hljs-string">Image</span> <span class="hljs-string">to</span> <span class="hljs-string">ECR</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          # Create repository if it doesn't exist (ignoring errors if it does)
          aws ecr create-repository --repository-name myapp || true
</span>
          <span class="hljs-comment"># Login to ECR</span>
          <span class="hljs-string">aws</span> <span class="hljs-string">ecr</span> <span class="hljs-string">get-login-password</span> <span class="hljs-string">|</span> <span class="hljs-string">docker</span> <span class="hljs-string">login</span> <span class="hljs-string">--username</span> <span class="hljs-string">AWS</span> <span class="hljs-string">--password-stdin</span> <span class="hljs-string">&lt;aws_account_id&gt;.dkr.ecr.us-east-1.amazonaws.com</span>

          <span class="hljs-comment"># Tag and push the image</span>
          <span class="hljs-string">docker</span> <span class="hljs-string">tag</span> <span class="hljs-string">myapp:latest</span> <span class="hljs-string">&lt;aws_account_id&gt;.dkr.ecr.us-east-1.amazonaws.com/myapp:latest</span>
          <span class="hljs-string">docker</span> <span class="hljs-string">push</span> <span class="hljs-string">&lt;aws_account_id&gt;.dkr.ecr.us-east-1.amazonaws.com/myapp:latest</span>
</code></pre>
<h3 id="heading-3-implement-zero-downtime-deployments">3. Implement Zero-Downtime Deployments</h3>
<p>Zero downtime is crucial. Kubernetes makes this easy with rolling updates:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Kubernetes deployment configured for zero-downtime updates</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">crud-app</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">3</span>  <span class="hljs-comment"># Multiple replicas for high availability</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">crud-app</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">crud-app</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">app</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">&lt;docker_registry&gt;/crud-app:latest</span>
        <span class="hljs-attr">ports:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">80</span>  <span class="hljs-comment"># Expose container port</span>
</code></pre>
<p>By having multiple replicas, you ensure that some pods stay live during updates.</p>
<h3 id="heading-4-create-cross-cloud-deployment-for-redundancy">4. Create Cross-Cloud Deployment for Redundancy</h3>
<p>If you want better reliability, you can deploy across different clouds in parallel:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Deploy to multiple cloud providers for redundancy</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">Cross-Cloud</span> <span class="hljs-string">Deployment</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">jobs:</span>
  <span class="hljs-comment"># Deploy to AWS</span>
  <span class="hljs-attr">aws-deploy:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</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">AWS</span> <span class="hljs-string">Setup</span> <span class="hljs-string">&amp;</span> <span class="hljs-string">Deploy</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          # Configure AWS CLI with credentials
          aws configure set aws_access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          # AWS deployment commands...
</span>
  <span class="hljs-comment"># Deploy to Oracle Cloud in parallel</span>
  <span class="hljs-attr">oracle-deploy:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</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">Oracle</span> <span class="hljs-string">Setup</span> <span class="hljs-string">&amp;</span> <span class="hljs-string">Deploy</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          # Configure Oracle Cloud CLI
          oci setup config
          # Oracle Cloud deployment commands...</span>
</code></pre>
<p>Now if one cloud goes down, the other is still up.</p>
<h3 id="heading-5-implement-automated-rollbacks-with-health-checks">5. Implement Automated Rollbacks with Health Checks</h3>
<p>Set up health checks so Kubernetes can automatically rollback if something goes wrong:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Deployment with health checks for automated rollbacks</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">crud-app</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">3</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">crud-app</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">crud-app</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">crud-app</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">&lt;docker_registry&gt;/crud-app:latest</span>
        <span class="hljs-attr">ports:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">80</span>
        <span class="hljs-comment"># Check if the container is alive</span>
        <span class="hljs-attr">livenessProbe:</span>
          <span class="hljs-attr">httpGet:</span>
            <span class="hljs-attr">path:</span> <span class="hljs-string">/healthz</span>  <span class="hljs-comment"># Health check endpoint</span>
            <span class="hljs-attr">port:</span> <span class="hljs-number">80</span>
          <span class="hljs-attr">initialDelaySeconds:</span> <span class="hljs-number">5</span>  <span class="hljs-comment"># Wait before first check</span>
          <span class="hljs-attr">periodSeconds:</span> <span class="hljs-number">10</span>       <span class="hljs-comment"># Check every 10 seconds</span>
        <span class="hljs-comment"># Check if the container is ready to receive traffic</span>
        <span class="hljs-attr">readinessProbe:</span>
          <span class="hljs-attr">httpGet:</span>
            <span class="hljs-attr">path:</span> <span class="hljs-string">/readiness</span>  <span class="hljs-comment"># Readiness check endpoint</span>
            <span class="hljs-attr">port:</span> <span class="hljs-number">80</span>
          <span class="hljs-attr">initialDelaySeconds:</span> <span class="hljs-number">5</span>  <span class="hljs-comment"># Wait before first check</span>
          <span class="hljs-attr">periodSeconds:</span> <span class="hljs-number">10</span>       <span class="hljs-comment"># Check every 10 seconds</span>
</code></pre>
<h2 id="heading-how-to-build-a-comprehensive-monitoring-system">How to Build a Comprehensive Monitoring System</h2>
<p>Even with a small deployment, monitoring is key to spotting issues early. So now, I’ll walk through setting up a comprehensive monitoring system for your application.</p>
<p>You'll learn how to integrate Grafana Cloud for visualizing your metrics, use Prometheus for collecting data, and configure custom alerts to monitor your app's performance. I’ll also cover tracking Service Level Objectives (SLOs) and setting up external monitoring with UptimeRobot to make sure that your endpoints are always available.</p>
<h3 id="heading-1-set-up-grafana-clouds-free-tier">1. Set Up Grafana Cloud's Free Tier</h3>
<p>Create a Grafana Cloud account and connect Prometheus as a data source. They offer generous free usage, which is perfect for small teams.</p>
<h3 id="heading-2-configure-prometheus-for-metrics-collection">2. Configure Prometheus for Metrics Collection</h3>
<p>Prometheus collects metrics from your app.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># prometheus.yml - Basic Prometheus configuration</span>
<span class="hljs-attr">global:</span>
  <span class="hljs-attr">scrape_interval:</span> <span class="hljs-string">15s</span>  <span class="hljs-comment"># Collect metrics every 15 seconds</span>
<span class="hljs-attr">scrape_configs:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">job_name:</span> <span class="hljs-string">'crud-app'</span>  <span class="hljs-comment"># Job name for the crud-app metrics</span>
    <span class="hljs-attr">static_configs:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">targets:</span> [<span class="hljs-string">'localhost:8080'</span>]  <span class="hljs-comment"># Where to collect metrics from</span>
</code></pre>
<p>This scrapes your app every 15 seconds for metrics.</p>
<h3 id="heading-3-create-monitoring-dashboards">3. Create Monitoring Dashboards</h3>
<p>Grafana visualizes Prometheus data. You can create dashboards using queries like:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Calculate average CPU usage rate per instance over 1 minute</span>
<span class="hljs-string">avg(rate(cpu_usage_seconds_total[1m]))</span> <span class="hljs-string">by</span> <span class="hljs-string">(instance)</span>
</code></pre>
<p>This calculates average CPU usage over the last minute per instance.</p>
<h3 id="heading-4-write-custom-promql-queries-for-alerts">4. Write Custom PromQL Queries for Alerts</h3>
<p>You can create smart alerts to detect increasing error rates, like the below:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Calculate error rate as a percentage of total requests</span>
<span class="hljs-comment"># Alert when error rate exceeds 5%</span>
<span class="hljs-string">sum(rate(http_requests_total{status=~"5.."}[5m]))</span> <span class="hljs-string">by</span> <span class="hljs-string">(service)</span>
  <span class="hljs-string">/</span> 
<span class="hljs-string">sum(rate(http_requests_total[5m]))</span> <span class="hljs-string">by</span> <span class="hljs-string">(service)</span> <span class="hljs-string">&gt;</span> <span class="hljs-number">0.05</span>
</code></pre>
<p>This alerts if more than 5% of your traffic results in errors.</p>
<h3 id="heading-5-implement-slo-tracking-on-a-budget">5. Implement SLO Tracking on a Budget</h3>
<p>You can track Service Level Objectives (SLOs) with Prometheus for free:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Calculate percentage of requests completed under 200ms</span>
<span class="hljs-comment"># Alert when it drops below 99%</span>
<span class="hljs-string">rate(http_request_duration_seconds_bucket{le="0.2"}[5m])</span> 
  <span class="hljs-string">/</span> <span class="hljs-string">rate(http_request_duration_seconds_count[5m])</span> 
<span class="hljs-string">&gt;</span> <span class="hljs-number">0.99</span>
</code></pre>
<p>This tracks if 99% of requests complete in under 200ms.</p>
<h3 id="heading-6-set-up-uptimerobot-for-external-monitoring">6. Set Up UptimeRobot for External Monitoring</h3>
<p>Finally, you can use UptimeRobot to check if your endpoints are reachable externally, and get alerts if anything goes down.</p>
<h2 id="heading-how-to-implement-security-testing-and-scanning">How to Implement Security Testing and Scanning</h2>
<p>Security should be integrated into your development pipeline from the start, not added as an afterthought. In this section, I’ll show you how to implement security testing and scanning at various stages of your workflow.</p>
<p>You’ll use GitHub CodeQL for static code analysis, OWASP ZAP for scanning web vulnerabilities, and Trivy for container image scanning. You’ll also learn how to enforce security thresholds directly in your CI pipeline.</p>
<h3 id="heading-1-enable-github-code-scanning-with-codeql">1. Enable GitHub Code Scanning with CodeQL</h3>
<p>GitHub has built-in code scanning with CodeQL<strong>.</strong> Here’s how to set it up:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># GitHub workflow for CodeQL security scanning</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">CodeQL</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">pull_request:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">analyze:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Analyze</span> <span class="hljs-string">code</span> <span class="hljs-string">with</span> <span class="hljs-string">CodeQL</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <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-comment"># Initialize the CodeQL scanning tools</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">CodeQL</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">github/codeql-action/init@v2</span>

      <span class="hljs-comment"># Run the analysis and generate results</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Analyze</span> <span class="hljs-string">code</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">github/codeql-action/analyze@v2</span>
</code></pre>
<p>This automatically checks your code for security vulnerabilities.</p>
<h3 id="heading-2-integrate-owasp-zap-into-your-ci-pipeline">2. Integrate OWASP ZAP into Your CI Pipeline</h3>
<p>You can also scan your deployed app with OWASP ZAP like this:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Automated security scanning with OWASP ZAP</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">ZAP</span> <span class="hljs-string">Scan</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">jobs:</span>
  <span class="hljs-attr">zap-scan:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <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-comment"># Run the ZAP security scan against deployed application</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">ZAP</span> <span class="hljs-string">security</span> <span class="hljs-string">scan</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">zaproxy/action-full-scan@v0.3.0</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">target:</span> <span class="hljs-string">'https://yourapp.com'</span>  <span class="hljs-comment"># URL to scan</span>
</code></pre>
<p>This checks for common web vulnerabilities.</p>
<h3 id="heading-3-set-up-trivy-for-container-vulnerability-scanning">3. Set Up Trivy for Container Vulnerability Scanning</h3>
<p>You can also check your container images for vulnerabilities with Trivy<strong>:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Scan Docker images for vulnerabilities using Trivy</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">Trivy</span> <span class="hljs-string">vulnerability</span> <span class="hljs-string">scanner</span>
  <span class="hljs-attr">uses:</span> <span class="hljs-string">aquasecurity/trivy-action@master</span>
  <span class="hljs-attr">with:</span>
    <span class="hljs-attr">image-ref:</span> <span class="hljs-string">'crud-app:latest'</span>   <span class="hljs-comment"># Image to scan</span>
    <span class="hljs-attr">format:</span> <span class="hljs-string">'table'</span>             <span class="hljs-comment"># Output format</span>
    <span class="hljs-attr">exit-code:</span> <span class="hljs-string">'1'</span>              <span class="hljs-comment"># Fail the build if vulnerabilities found</span>
    <span class="hljs-attr">ignore-unfixed:</span> <span class="hljs-literal">true</span>        <span class="hljs-comment"># Skip vulnerabilities without fixes</span>
    <span class="hljs-attr">severity:</span> <span class="hljs-string">'CRITICAL,HIGH'</span>   <span class="hljs-comment"># Only alert on critical and high severity</span>
</code></pre>
<p>Your builds will fail if serious issues are found, keeping you safe by default.</p>
<h3 id="heading-4-create-threshold-based-pipeline-failures">4. Create Threshold-Based Pipeline Failures</h3>
<p>You can configure your pipelines to fail automatically if vulnerabilities exceed a set threshold, enforcing strong security practices without manual effort. Here’s how that should look:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Fail the pipeline if critical or high vulnerabilities are found</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">Trivy</span> <span class="hljs-string">vulnerability</span> <span class="hljs-string">scanner</span>
  <span class="hljs-attr">uses:</span> <span class="hljs-string">aquasecurity/trivy-action@master</span>
  <span class="hljs-attr">with:</span>
    <span class="hljs-attr">image-ref:</span> <span class="hljs-string">'crud-app:latest'</span>   <span class="hljs-comment"># Image to scan</span>
    <span class="hljs-attr">format:</span> <span class="hljs-string">'json'</span>              <span class="hljs-comment"># Output as JSON for parsing</span>
    <span class="hljs-attr">exit-code:</span> <span class="hljs-string">'1'</span>              <span class="hljs-comment"># Fail the build if vulnerabilities found</span>
    <span class="hljs-attr">severity:</span> <span class="hljs-string">'CRITICAL,HIGH'</span>   <span class="hljs-comment"># Check for critical and high severity issues</span>
    <span class="hljs-attr">ignore-unfixed:</span> <span class="hljs-literal">true</span>        <span class="hljs-comment"># Skip vulnerabilities without fixes</span>
</code></pre>
<p>This forces a no-compromise security posture – that is, if critical or high vulnerabilities are detected, the build stops immediately.</p>
<h3 id="heading-5-implement-custom-security-checks">5. Implement Custom Security Checks</h3>
<p>Sometimes you need to go beyond automated scanners. Here's a basic example of a custom security check you can add to your pipeline:</p>
<pre><code class="lang-yaml"><span class="hljs-comment">#!/bin/bash</span>

<span class="hljs-comment"># Custom script to check for hard-coded secrets in source code</span>
<span class="hljs-comment"># Check for hard-coded API keys in source files</span>
<span class="hljs-string">if</span> <span class="hljs-string">grep</span> <span class="hljs-string">-r</span> <span class="hljs-string">"API_KEY"</span> <span class="hljs-string">./src;</span> <span class="hljs-string">then</span>
  <span class="hljs-string">echo</span> <span class="hljs-string">"Security issue: Found hard-coded API keys."</span>
  <span class="hljs-string">exit</span> <span class="hljs-number">1</span>  <span class="hljs-comment"># Fail the build</span>
<span class="hljs-string">else</span>
  <span class="hljs-string">echo</span> <span class="hljs-string">"No hard-coded API keys found."</span>
<span class="hljs-string">fi</span>
</code></pre>
<p>You can extend this script to scan for patterns like private keys, passwords, or other sensitive information, helping catch issues before they ever reach production.</p>
<h2 id="heading-performance-optimization-and-scaling">Performance Optimization and Scaling</h2>
<p>Optimizing early saves you pain later. Here’s how to make your pipelines faster, smarter, and more scalable:</p>
<h3 id="heading-1-measure-pipeline-execution-times">1. Measure Pipeline Execution Times</h3>
<p>Understanding how long each step takes is the first step to improving it:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-comment"># Record the start time</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Start</span> <span class="hljs-string">timer</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"Start time: $(date)"</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>

      <span class="hljs-comment"># Record the end time to calculate duration</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">End</span> <span class="hljs-string">timer</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"End time: $(date)"</span>
</code></pre>
<p>Later, you can automate time tracking for full reports and alerts.</p>
<h3 id="heading-2-implement-parallelization-strategies">2. Implement Parallelization Strategies</h3>
<p>Split your jobs smartly to save time:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">jobs:</span>
  <span class="hljs-comment"># First job to install dependencies</span>
  <span class="hljs-attr">install:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span>

  <span class="hljs-comment"># Run tests in parallel with linting</span>
  <span class="hljs-attr">test:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">install</span>  <span class="hljs-comment"># Depends on install job</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">test</span>

  <span class="hljs-comment"># Run linting in parallel with tests</span>
  <span class="hljs-attr">lint:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">install</span>  <span class="hljs-comment"># Also depends on install job</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">lint</span>
</code></pre>
<p>Result: Testing and linting run in parallel after installing dependencies, cutting pipeline time significantly.</p>
<h3 id="heading-3-set-up-distributed-caching">3. Set Up Distributed Caching</h3>
<p>Caching saves your workflow from repeating expensive tasks:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Cache dependencies to speed up builds</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Cache</span> <span class="hljs-string">node</span> <span class="hljs-string">modules</span>
  <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/cache@v3</span>
  <span class="hljs-attr">with:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">|
      ~/.npm           # Cache global npm cache
      node_modules     # Cache local dependencies
</span>    <span class="hljs-attr">key:</span> <span class="hljs-string">${{</span> <span class="hljs-string">runner.os</span> <span class="hljs-string">}}-node-${{</span> <span class="hljs-string">hashFiles('**/package-lock.json')</span> <span class="hljs-string">}}</span>  <span class="hljs-comment"># Key based on OS and dependency hash</span>
    <span class="hljs-attr">restore-keys:</span> <span class="hljs-string">|</span>    <span class="hljs-comment"># Fallback keys if exact match isn't found</span>
      <span class="hljs-string">${{</span> <span class="hljs-string">runner.os</span> <span class="hljs-string">}}-node-</span>
</code></pre>
<p><strong>Tip:</strong> Also cache build artifacts, Docker layers, and Terraform plans when possible.</p>
<h3 id="heading-4-create-performance-benchmarks">4. Create Performance Benchmarks</h3>
<p>Track your build times over time with benchmarks:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-comment"># Store the start time as an environment variable</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Start</span> <span class="hljs-string">timer</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">start_time</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"start_time=$(date +%s)"</span> <span class="hljs-string">&gt;&gt;</span> <span class="hljs-string">$GITHUB_ENV</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>

      <span class="hljs-comment"># Calculate and display the elapsed time</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">End</span> <span class="hljs-string">timer</span> <span class="hljs-string">and</span> <span class="hljs-string">calculate</span> <span class="hljs-string">elapsed</span> <span class="hljs-string">time</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          end_time=$(date +%s)
          elapsed_time=$((end_time - ${{ env.start_time }}))
          echo "Build time: $elapsed_time seconds"</span>
</code></pre>
<p>With benchmarks in place, you can monitor regressions and trigger optimizations automatically.</p>
<h3 id="heading-5-how-to-plan-for-growth-beyond-free-tiers">5. How to Plan for Growth Beyond Free Tiers</h3>
<ul>
<li><p><strong>Understand cloud pricing structures:</strong> AWS, Azure, GCP all offer generous free tiers, but know the limits to avoid surprise bills. <em>(I have been there and it wasn’t pretty.)</em></p>
</li>
<li><p><strong>Consider scaling to more advanced CI/CD tools:</strong> Jenkins, CircleCI, GitLab can offer better performance or self-hosted control as you grow.</p>
</li>
<li><p><strong>Automate resource provisioning:</strong> Use Infrastructure as Code (IaC) with Terraform, Pulumi, or AWS CDK to dynamically scale your infrastructure when your team or traffic grows.</p>
</li>
</ul>
<h2 id="heading-complete-cicd-pipeline-example">Complete CI/CD Pipeline Example</h2>
<p>Here’s a full example tying everything together:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Complete end-to-end CI/CD pipeline</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">CI/CD</span> <span class="hljs-string">Pipeline</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">jobs:</span>
  <span class="hljs-comment"># Initial setup job</span>
  <span class="hljs-attr">setup:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <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-comment"># Build and test job</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">setup</span>  <span class="hljs-comment"># Depends on setup job</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</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-string">'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">security</span> <span class="hljs-string">scan</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npx</span> <span class="hljs-string">eslint</span> <span class="hljs-string">.</span>  <span class="hljs-comment"># Run ESLint for security rules</span>

  <span class="hljs-comment"># Deploy to Kubernetes job</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">needs:</span> <span class="hljs-string">build</span>  <span class="hljs-comment"># Depends on successful build</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">K3d</span> <span class="hljs-string">cluster</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">k3d</span> <span class="hljs-string">cluster</span> <span class="hljs-string">create</span> <span class="hljs-string">dev-cluster</span> <span class="hljs-string">--servers</span> <span class="hljs-number">1</span> <span class="hljs-string">--agents</span> <span class="hljs-number">2</span> <span class="hljs-string">--port</span> <span class="hljs-number">8080</span><span class="hljs-string">:80@loadbalancer</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Apply</span> <span class="hljs-string">Kubernetes</span> <span class="hljs-string">manifests</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">kubectl</span> <span class="hljs-string">apply</span> <span class="hljs-string">-f</span> <span class="hljs-string">k8s/</span>  <span class="hljs-comment"># Apply all K8s manifests in the k8s directory</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">app</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">kubectl</span> <span class="hljs-string">rollout</span> <span class="hljs-string">restart</span> <span class="hljs-string">deployment/webapp</span>  <span class="hljs-comment"># Restart deployment for zero-downtime update</span>

  <span class="hljs-comment"># Infrastructure provisioning job</span>
  <span class="hljs-attr">terraform:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">deploy</span>  <span class="hljs-comment"># Run after deployment</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Terraform</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">hashicorp/setup-terraform@v2</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Terraform</span> <span class="hljs-string">Init</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">terraform</span> <span class="hljs-string">init</span>  <span class="hljs-comment"># Initialize Terraform</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Terraform</span> <span class="hljs-string">Apply</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">terraform</span> <span class="hljs-string">apply</span> <span class="hljs-string">-auto-approve</span>  <span class="hljs-comment"># Apply infrastructure changes automatically</span>
</code></pre>
<h4 id="heading-runbook-failed-deployment"><strong>Runbook: Failed Deployment:</strong></h4>
<p><strong>Issue</strong>: Pods fail due to resource limits (for example, OOMKilled, CrashLoopBackOff).<br><strong>Fix</strong>:</p>
<pre><code class="lang-yaml">  <span class="hljs-string">kubectl</span> <span class="hljs-string">top</span> <span class="hljs-string">pod</span>
  <span class="hljs-string">kubectl</span> <span class="hljs-string">edit</span> <span class="hljs-string">deployment</span> <span class="hljs-string">crud-app</span>
  <span class="hljs-string">kubectl</span> <span class="hljs-string">apply</span> <span class="hljs-string">-f</span> <span class="hljs-string">deployment.yaml</span>
  <span class="hljs-string">kubectl</span> <span class="hljs-string">rollout</span> <span class="hljs-string">status</span> <span class="hljs-string">deployment/crud-app</span>
</code></pre>
<p><strong>Tip:</strong> Set realistic resource requests and limits early, it'll save you debugging time later.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>By following along with this tutorial, you now know how to build a production-ready DevOps pipeline using free tools:</p>
<ul>
<li><p><strong>CI/CD</strong>: GitHub Actions for testing, linting, and building.</p>
</li>
<li><p><strong>Infrastructure</strong>: Terraform for AWS/Render and PostgreSQL setup.</p>
</li>
<li><p><strong>Orchestration</strong>: K3d for local Kubernetes.</p>
</li>
<li><p><strong>Monitoring</strong>: Grafana, Prometheus, UptimeRobot.</p>
</li>
<li><p><strong>Security</strong>: CodeQL, OWASP ZAP, Trivy for vulnerability scanning.</p>
</li>
</ul>
<p>This pipeline is scalable and secure, and it’s perfect for small projects. As your app grows, you might want to consider paid plans for more resources (for example, AWS larger instances, Grafana unlimited metrics). You can check <a target="_blank" href="https://aws.amazon.com/free/">AWS Free Tier</a>, <a target="_blank" href="https://developer.hashicorp.com/terraform/docs">Terraform Docs</a>, and <a target="_blank" href="https://grafana.com/docs/">Grafana Docs</a> for more learning.</p>
<p><strong>PS:</strong> I’d love to see what you build. Share your pipeline on <a target="_blank" href="https://forum.freecodecamp.org/">FreeCodeCamp’s forum</a> or tag me on X <a target="_blank" href="https://x.com/Emidowojo">@Emidowojo</a> with #DevOpsOnABudget, and tell me about the challenges you faced. You can also connect with me on <a target="_blank" href="https://www.linkedin.com/in/emidowojo/">LinkedIn</a> if you’d like to stay in touch. If you made it to the end of this lengthy article, thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Multilingual Social Recipe Application with Flutter and Strapi ]]>
                </title>
                <description>
                    <![CDATA[ Hey there! In this project, you will build a multilingual social recipe application using Flutter and Strapi. Flutter is an open-source UI software development kit created by Google. It allows you to build beautiful and highly interactive user interf... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-multilingual-social-recipe-app-with-flutter-and-strapi/</link>
                <guid isPermaLink="false">67f59a4c27d15057ec14c438</guid>
                
                    <category>
                        <![CDATA[ Recipe Apps ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Strapi ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flutter ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ multilingual ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kevine Nzapdi ]]>
                </dc:creator>
                <pubDate>Tue, 08 Apr 2025 21:51:08 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1743509325302/fd7d5d6c-9a48-4037-9cc2-3b35a92b6006.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Hey there!</p>
<p>In this project, you will build a multilingual social recipe application using Flutter and Strapi.</p>
<p>Flutter is an open-source UI software development kit created by Google. It allows you to build beautiful and highly interactive user interfaces for mobile, web, and desktop from a single codebase.</p>
<p>Strapi, on the other hand, is a headless CMS that makes it easy to create, manage and distribute content anywhere you need – all from one place.</p>
<p>The multilingual feature of the application will allow users from different parts of the world to interact with the app in their native language, making it more user-friendly and accessible. This feature is particularly beneficial for a social recipe application where users share recipes from different cuisines and cultures.</p>
<p>In this application, users will be able to view recipes, request a specific recipe, share their favorite recipes, and like or comment on recipes.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-demo">Demo</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-create-models">Create Models</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-add-languages-and-enable-internationalization-in-strapi">Add Languages and Enable Internationalization in Strapi</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-add-recipe-content">Add Recipe Content</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-add-recipe-english-content">Add Recipe English Content</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-add-recipe-french-content">Add Recipe French Content</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-add-recipe-japanese-content">Add Recipe Japanese Content</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-generate-api-token-and-set-permissions">Generate API Token and Set permissions</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-set-user-roles-and-permissions">Set User Roles and Permissions</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-set-up-flutter">Set up Flutter</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-project-structure">Project Structure</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-install-packages">Install Packages</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-add-assets">Add Assets</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-taking-a-look-at-maindart">Taking a look at main.dart</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-add-environment-variables">Add Environment Variables</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-create-models-1">Create Models</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-reciperequest">1. RecipeRequest</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-step">2. Step</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-description">3. Description</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-textcontent">4. TextContent</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-5-comment">5. Comment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-6-recipe">6. Recipe</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-create-services">Create Services</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-class-variables">1. Class Variables</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-helper-methods">2. Helper Methods</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-user-operations">3. User Operations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-data-fetching-and-manipulation">4. Data Fetching and Manipulation</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-authorization-and-authentication">Authorization and Authentication</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-registration">Registration</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-login">Login</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-build-app-components">Build App Components</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-drawer">Drawer</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-appbar">AppBar</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-fetch-recipes">Fetch Recipes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-view-recipe">View Recipe</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-create-request-recipe-screen">Create Request Recipe Screen</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-create-user-profile-screen">Create User Profile Screen</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-test-the-app">Test the App</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-references">References</a></p>
</li>
</ol>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along with this tutorial, make sure you have:</p>
<ul>
<li><p><a target="_blank" href="https://nodejs.org/en">Node.js</a> installed.</p>
</li>
<li><p>Basic knowledge of <a target="_blank" href="https://flutter.dev/">Flutter</a></p>
</li>
<li><p>Basic understanding of Strapi with this <a target="_blank" href="https://docs.strapi.io/dev-docs/quick-start">quick guide</a></p>
</li>
</ul>
<h2 id="heading-demo">Demo</h2>
<p>Here’s what you will be building in the tutorial:</p>
<ol>
<li><p>Authentication and Authorization: <a target="_blank" href="https://drive.google.com/file/d/1cjnnRD38wQsj_sYHl5EG5uM3AyHJUWdf/view?usp=sharing">Demo</a></p>
</li>
<li><p>Comment and Likes: <a target="_blank" href="https://drive.google.com/file/d/1wM0xQ2R7inL90gAkiYjLcGV5df4AmzH1/view?usp=sharing">Demo</a></p>
</li>
<li><p>Request recipe: <a target="_blank" href="https://drive.google.com/file/d/1xlxSFD2qU2rOE4kICiX-py_JxvgrphqK/view?usp=sharing">Demo</a></p>
</li>
<li><p>Language Switch: <a target="_blank" href="https://drive.google.com/file/d/14lmBCIgX4VIKOFmS9pG71cIHH7HLaW1J/view?usp=sharing">Demo</a></p>
</li>
</ol>
<p>You can get the full code of the application from <a target="_blank" href="https://github.com/Gunkev/flutter_strapi_multilingual_app">this GitHub repository</a>.</p>
<h2 id="heading-create-models">Create Models</h2>
<p>Once you have set up a Strapi project with <a target="_blank" href="https://docs.strapi.io/dev-docs/installation/cli">this quick guide</a>, create two models, Recipe and RecipeRequest, in the Strapi admin panel.</p>
<p>A recipe typically has the following elements:</p>
<ul>
<li><p>Title: <code>text</code> which represents the title of the recipe</p>
</li>
<li><p>Ingredients: <code>text</code> which represent the of ingredients of the recipe</p>
</li>
<li><p>Likes: <code>int</code> which represent the number of likes</p>
</li>
<li><p>Author: <code>relation</code> which represent the author of the recipe</p>
</li>
<li><p>Comments: <code>relation</code> which represent the list of comments of a specific recipe</p>
</li>
<li><p>Steps: <code>rich text</code> which represents the main content of the recipe</p>
</li>
<li><p>Description: <code>rich text</code> which represents a description of what the recipe is like</p>
</li>
<li><p>Comment Count: <code>int</code> which represents the number of comment a recipe has</p>
</li>
<li><p>Cover Image: <code>media</code> which represents the cover image of the recipe</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743504946186/e1be7d98-fff8-4e2e-b446-1ddbf541d1c0.png" alt="recipe model" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Make sure to enable internationalization for Recipe Content Type when you create it:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743504992503/73842540-4b8d-4412-9c51-1c55e095e83e.png" alt="enable internationalization" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>A recipe request typically has:</p>
<ul>
<li><p>Title, which is <code>text</code> that represents the title of the request</p>
</li>
<li><p>Description, which is <code>rich text</code> that represents the content of the request</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505019316/6d172672-af58-4a6d-b0a3-cb713ee32dd2.png" alt="recipe request model" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>A comment typical has:</p>
<ul>
<li><p>Author, which is a <code>relation</code> that represents the author of the comment</p>
</li>
<li><p>Content, which is <code>text</code> that represents the content of the comments</p>
</li>
<li><p>Date, which is a <code>date</code> that represents the published date of the comment</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505036935/92d02ecb-9a86-43f9-99a9-a2a534aab871.png" alt="comment model" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>The user will also have 4 new fields:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505060587/cda0be86-298b-4053-b8ae-8c894e07a592.png" alt="additional user fields" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-add-languages-and-enable-internationalization-in-strapi">Add Languages and Enable Internationalization in Strapi</h2>
<p>The application will support three different languages (English, French, and Japanese). English is the default language, so you need to add the two others. In the Strapi panel, you’ll need to navigate to Settings and then Internationalization and add French and Japanese. I will explain the process in detail in the next sections.</p>
<h2 id="heading-add-recipe-content">Add Recipe Content</h2>
<p>Next, you will populate some recipe data in English, French, and Japanese.</p>
<h3 id="heading-add-recipe-english-content">Add Recipe English Content</h3>
<p>Since English is the default language, go to Content manager, then select Recipe, and then select <strong>Create new entry</strong>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505111608/3fb2d615-d649-4c22-8a73-87cbcbd38bdb.png" alt="list of added recipes" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-add-recipe-french-content">Add Recipe French Content</h3>
<p>For French, navigate to Settings, select Internationalization, and then under global settings click on <strong>Add new locale.</strong> Here you will add the French language.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505140738/a8e5b0d0-0871-46b1-8fb0-2921c84b913a.png" alt="french language config" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Back to the Content manager, click on recipe and select the French language in the top right corner. Then choose the <strong>Create recipe entry</strong> in French.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505164770/2ad75e5a-a20d-496d-9fe3-75fdc3cf64b1.png" alt="french model version" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-add-recipe-japanese-content">Add Recipe Japanese Content</h3>
<p>Navigate back to Settings and Internationalization, and under global settings again click on <strong>Add new locale.</strong> Now you will add the Japanese language.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505187987/91251e4e-4172-4ce5-9e53-78ca12352af4.png" alt="japanese language config" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Back to the Content manager, click on recipe and select the Japanese language in the top right corner. Then select <strong>Create new entry</strong> in Japanese.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505218903/0e7b7025-8473-4012-ab54-130fe5b63164.png" alt="Japenese recipe list" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-generate-api-token-and-set-permissions">Generate API Token and Set permissions</h2>
<p>Once you’ve added the content for the various languages, it’s time to create your API and set the necessary permissions.</p>
<p>To do this, navigate to Settings, then API Tokens, and then Create API Token. Add the details of your key there.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505239235/5a183f54-6469-4d4e-aa62-d81f4dccf8ae.png" alt="API token creation" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<ul>
<li><p>Token duration: choose Unlimited</p>
</li>
<li><p>Token Type: Custom. The custom type allows you to specify permission for certain entities.</p>
</li>
</ul>
<p>Next, still in the Create API Token screen, scroll down to the permission section and set the permission to “Select all” for Comments, and RecipeRequest, upload, email, content type, i18n, and User permissions like in the screenshot below for Recipe-request:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505260256/84f6f009-4c7a-4136-8497-6c22b9fa87de.png" alt="enable permission for recipe request" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744116611459/f5518d2e-5200-40b3-9b74-ed0b0adeeabb.png" alt="f5518d2e-5200-40b3-9b74-ed0b0adeeabb" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Then click on the Save button in the top right corner to generate your API key. Copy and save the key in your PC as you won’t be able to see it again</p>
<h3 id="heading-set-user-roles-and-permissions">Set User Roles and Permissions</h3>
<p>You’ll also need to set the user roles and permissions using the <a target="_blank" href="https://docs.strapi.io/dev-docs/plugins/users-permissions">User and Permission Plugin</a>. It allows you to manage what both authenticated and non-authenticated users can do in your application.</p>
<p>Head to the Settings section of the dashboard and go to Roles under the User and Permission plugin.</p>
<p>We have two types of users:</p>
<ul>
<li><p>Authenticated users</p>
</li>
<li><p>Public users</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744117848867/8023d7c4-c07b-43dc-ba00-89a958bc0672.png" alt="8023d7c4-c07b-43dc-ba00-89a958bc0672" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Select the authenticated users and give them the following permissions for:</p>
<p>Comment:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505301527/3939448a-48f4-44fc-baa9-a528a78e73c7.png" alt="enable permission for comments" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Recipe:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505327113/f9224713-105d-4cdb-9a5b-4846d1789b07.png" alt="enable authorized user to perdorm action on recipe model" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Request-recipe:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505346092/d328c629-4ea9-40a0-baa6-90a96ae364ec.png" alt="enable permission for recipe request model" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Also select all for Content-type builder, i18n, and Upload and then save.</p>
<p>Public users can only read recipes and comments:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505362706/4d776b8f-84f9-4a41-a1d4-73b1a2fd6a4c.png" alt="limit comment operation for public users" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505369235/54ed5f73-9841-43bf-a088-0079358b6b05.png" alt="limit recipe operations for public user" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-set-up-flutter">Set Up Flutter</h2>
<p>Once you have <a target="_blank" href="https://docs.flutter.dev/get-started/install/windows/desktop">set up Flutte</a><a target="_blank" href="https://docs.flutter.dev/get-started/install/windows/desktop">r</a> in your environment, run the following command to bootstrap a new application in your favorite directory:</p>
<pre><code class="lang-bash">flutter create flutter_recipe_app
</code></pre>
<p>To see your app in action, you need to run it on a mobile device. You can either:</p>
<ul>
<li><p>Use an <strong>emulator</strong> (a virtual Android or iOS device that runs on your computer), or</p>
</li>
<li><p>Connect a <strong>physical device</strong> (like your smartphone) to your computer with a USB cable.</p>
</li>
</ul>
<p>Once your emulator or device is ready, navigate into the newly created project folder:</p>
<pre><code class="lang-bash">flutter run
</code></pre>
<p>This command builds the app and starts it on your connected device or emulator.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1743505498936/6e1e461d-9fee-4e19-81e0-65d25ddebd63.png" alt="flutter starter app" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-project-structure">Project Structure</h3>
<p>Now let's look at the file structure of the project:</p>
<pre><code class="lang-bash">flutter_recipe_app/
|
|-- .dart_tool/
|-- .idea/
|-- android/ [flutter_recipe_app_android]
|   |-- assets/
|   |   |-- images/
|   |   |-- translations/
|
|-- build/
|-- ios/
|-- lib/
|   |-- components/
|   |   |-- appBar.dart
|   |   |-- drawer.dart
|   |
|   |-- models/
|   |   |-- recipe.dart
|   |
|   |-- screens/
|   |   |-- detail.dart
|   |   |-- home.dart
|   |   |-- login.dart
|   |   |-- profile.dart
|   |   |-- requestRecipe.dart
|   |   |-- signUp.dart
|   |
|   |-- utils/
|       |-- server2.dart
|
|-- main.dart
|-- <span class="hljs-built_in">test</span>/
|-- .env
</code></pre>
<p>The structure is organized as follows:</p>
<ul>
<li><p><code>.dart_tool/</code>: Contains Dart tools and build outputs.</p>
</li>
<li><p><code>.idea/</code>: IDE-specific settings.</p>
</li>
<li><p><code>android/</code>: Android-specific project files, including custom assets like images and translations.</p>
</li>
<li><p><code>build/</code>: Generated files from the build process.</p>
</li>
<li><p><code>ios/</code>: iOS-specific project files.</p>
</li>
<li><p><code>lib/</code>: The main source directory for Dart code, which includes:</p>
<ul>
<li><p><code>components/</code>: Reusable widgets or UI components like <code>appBar</code> and <code>drawer</code>.</p>
</li>
<li><p><code>models/</code>: Data models for your application, like <code>recipe</code>.</p>
</li>
<li><p><code>screens/</code>: Individual screens of the app, such as the <code>recipe details</code>, <code>home</code>, <code>login</code>, <code>profile</code>, <code>request recipe</code> and <code>signUp</code> screens of the app</p>
</li>
<li><p><code>utils/</code>: Utilities and helper functions, like <code>server2.dart</code> for the server communication logic.</p>
</li>
</ul>
</li>
<li><p><code>main.dart</code>: The entry point of the Flutter application.</p>
</li>
<li><p><code>test/</code>: Directory for test files.</p>
</li>
<li><p><code>.env</code>: Environment-specific variables file.</p>
</li>
</ul>
<p>This setup is typical for a moderately complex Flutter application, segregating functionality into manageable, logical sections for better organization and maintainability.</p>
<h2 id="heading-install-packages">Install Packages</h2>
<p>In this tutorial, we’re using five main packages:</p>
<ul>
<li><p><a target="_blank" href="https://pub.dev/packages/flutter_dotenv">flutter_dotenv</a>: to manage environment variables</p>
</li>
<li><p><a target="_blank" href="https://pub.dev/packages/http">http</a>: to handle HTTP requests and interact with <a target="_blank" href="https://docs.strapi.io/dev-docs/api/rest">Strapi REST API</a></p>
</li>
<li><p><a target="_blank" href="https://pub.dev/packages/shared_preferences">shared_preferences</a>: persists key-value data on the device like user login tokens</p>
</li>
<li><p><a target="_blank" href="https://pub.dev/packages/provider">provider</a>: for state management and updating your UI reactively when the underlying state changes</p>
</li>
<li><p><a target="_blank" href="https://pub.dev/packages/easy_localization">easy_localization</a>: for managing translations and locale data. It supports both JSON and YAML file formats for defining translations.</p>
</li>
</ul>
<p>In your <code>pubspec.yaml</code> file, add the following lines:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">dependencies:</span>
  <span class="hljs-attr">flutter:</span>
    <span class="hljs-string">...</span>
  <span class="hljs-attr">flutter_dotenv:</span> <span class="hljs-string">^5.1.0</span>
  <span class="hljs-attr">http:</span> <span class="hljs-string">^1.1.0</span>
  <span class="hljs-attr">shared_preferences:</span> <span class="hljs-string">^2.2.2</span>
  <span class="hljs-attr">provider:</span> <span class="hljs-string">^6.1.2</span>
  <span class="hljs-attr">easy_localization:</span> <span class="hljs-string">^3.0.7</span>
</code></pre>
<p>Then run the command below to install the packages:</p>
<pre><code class="lang-bash">flutter pub get
</code></pre>
<h3 id="heading-add-assets">Add Assets</h3>
<p>Add the path to your assets in your <code>pubspec.yaml</code> file found at the root of your project:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">flutter:</span>
  <span class="hljs-attr">uses-material-design:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">assets:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">.env</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">assets/translations/</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">assets/images/</span>
</code></pre>
<p>The translations folder contains the list of your translations while the images folder hosts the photos of your application.</p>
<h3 id="heading-taking-a-look-at-maindart">Taking a look at main.dart</h3>
<p>In the <code>main.dart</code> file, you need to set up your localization, load environment variables, and a list of providers for dependency injection:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:easy_localization/easy_localization.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_recipe_app/screens/home.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_recipe_app/screens/login.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_recipe_app/screens/requestRecipe.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_recipe_app/screens/signUp.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_recipe_app/utils/server.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:provider/provider.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_dotenv/flutter_dotenv.dart'</span>;

Future&lt;<span class="hljs-keyword">void</span>&gt; main() <span class="hljs-keyword">async</span>{
  <span class="hljs-comment">// Ensure all bindings are initialized</span>
  WidgetsFlutterBinding.ensureInitialized();
  <span class="hljs-keyword">await</span> EasyLocalization.ensureInitialized();

  <span class="hljs-comment">// Load environment variables</span>
  <span class="hljs-keyword">await</span> dotenv.load(fileName: <span class="hljs-string">".env"</span>);
  runApp(EasyLocalization(
    supportedLocales: <span class="hljs-keyword">const</span> [
      Locale(<span class="hljs-string">'en'</span>),
      Locale(<span class="hljs-string">'fr'</span>, <span class="hljs-string">'FR'</span>),
      Locale(<span class="hljs-string">'ja'</span>, <span class="hljs-string">'JP'</span>)],
    path: <span class="hljs-string">'assets/translations'</span>, <span class="hljs-comment">//</span>
    fallbackLocale: Locale(<span class="hljs-string">'en'</span>),
    child: MyApp(),
  ));
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApp</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> MultiProvider(
      providers: [
        Provider(create: (_) =&gt; ApiService()),
      ],
      child: MaterialApp(
        title: tr(<span class="hljs-string">'app_description'</span>),
        localizationsDelegates: context.localizationDelegates,
        supportedLocales: context.supportedLocales,
        locale: context.locale,
        initialRoute: <span class="hljs-string">'/home'</span>,
        routes: {
          <span class="hljs-string">'/request'</span>: (context) =&gt; RecipeRequestScreen(),
          <span class="hljs-string">'/login'</span>: (context) =&gt; LoginScreen(),
          <span class="hljs-string">'/register'</span>: (context) =&gt; RegisterScreen(),
          <span class="hljs-string">'/home'</span>: (context) =&gt; HomeScreen(), <span class="hljs-comment">// Implement HomeScreen</span>
        },
      ),
    );
  }
}
</code></pre>
<p>From the code snippet above, the <code>WidgetsFlutterBinding.ensureInitialized()</code> ensures that all Flutter bindings are initialized before any other operations and the <code>EasyLocalization.ensureInitialized()</code> initializes the EasyLocalization package to handle translations.</p>
<p>Load the environment variables with <code>dotenv.load(fileName: ".env")</code> to read variables from the <code>.env</code> file. The <code>runApp</code> function wraps the <code>MyApp</code> widget with the <code>EasyLocalization</code> widget, which is configured to support English (<code>en</code>), French (<code>fr_FR</code>), and Japanese (<code>ja_JP</code>) locales. The path for translation files is set to <code>'assets/translations'</code>, and the fallback locale is set to English.</p>
<p>It also creates the main routes of the recipe application and sets <code>home</code> as the initial route.</p>
<h2 id="heading-add-environment-variables">Add Environment Variables</h2>
<p>You will store configuration data such as API keys, environment-specific URLs (base URL, recipe endpoints, comments endpoints), and other sensitive or configurable data outside your codebase using the <code>flutter_dotenv</code> package you installed earlier. Create an <code>.env</code> file in your root directory and add your environment variables:</p>
<pre><code class="lang-bash">BASE_URL=your-base-url
USERS_ENDPOINT=/auth/<span class="hljs-built_in">local</span>
USERS_ENDPOINT_REG=/auth/<span class="hljs-built_in">local</span>/register
ACCESS_TOKEN=your-api-key
RECIPE_ENDPOINT=/recipes
COMMENT_ENDPOINT=/comments
R_REQUEST_ENDPOINT=/recipe-requests
</code></pre>
<ul>
<li><p><code>BASE_URL</code>: This is the base URL for your Strapi backend server. The <code>/api</code> means that all API endpoints are accessed via this base path. This URL is used to construct full URLs for all API requests by appending specific endpoints to it.</p>
</li>
<li><p><code>USERS_ENDPOINT</code>: This endpoint typically handles login operations where existing users authenticate by submitting their credentials.</p>
</li>
<li><p><code>USERS_ENDPOINT_REG</code>: This is the registration endpoint for new users.</p>
</li>
<li><p><code>ACCESS_TOKEN</code>: This is the API token you created earlier which is used for authenticating API requests.</p>
</li>
<li><p><code>RECIPE_ENDPOINT</code>: This endpoint is used to fetch a list of recipes or a single recipe. You can also use it to post new recipes, or update or delete a recipe.</p>
</li>
<li><p><code>COMMENT_ENDPOINT</code>: This endpoint manages comments related to recipes.</p>
</li>
<li><p><code>R_REQUEST_ENDPOINT</code>: This endpoint manages requests related to recipes.</p>
</li>
</ul>
<h2 id="heading-create-models-1">Create Models</h2>
<p>Here you will create the different models of the app. You can create all the models in a single file or create them in individual files. In this tutorial, we’ll create all the models in a single file which is <code>lib/models/recipe.dart</code>:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_dotenv/flutter_dotenv.dart'</span>;

<span class="hljs-comment">// models recipe_ request</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RecipeRequest</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> id;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> title;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;Description&gt; description

  RecipeRequest({
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.id,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.title,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.description,
  });

  <span class="hljs-keyword">factory</span> RecipeRequest.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) {
    <span class="hljs-keyword">var</span> attr = json[<span class="hljs-string">'attributes'</span>] ?? {};
    <span class="hljs-keyword">var</span> attributes = json[<span class="hljs-string">'attributes'</span>] ?? {};
    <span class="hljs-built_in">List</span>&lt;Description&gt; descriptionList = (attr[<span class="hljs-string">'description'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">List?</span> ?? [])
        .map((desc) =&gt; Description.fromJson(desc)).toList();

    <span class="hljs-built_in">print</span>(<span class="hljs-string">"Parsed Recipe: <span class="hljs-subst">${json[<span class="hljs-string">'id'</span>]}</span> - Descriptions: <span class="hljs-subst">${descriptionList.length}</span>"</span>);

    <span class="hljs-keyword">return</span> RecipeRequest(
      id: json[<span class="hljs-string">'id'</span>] ?? <span class="hljs-number">0</span>,
      title: attr[<span class="hljs-string">'title'</span>] ?? <span class="hljs-string">'No title'</span>,
      description: descriptionList,
    );
  }

  <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; toJson() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-string">'title'</span>: title,
      <span class="hljs-string">'description'</span>: description.map((desc) =&gt; desc.toJson()).toList(),
      <span class="hljs-comment">// 'id': id</span>
    };
  }
}

<span class="hljs-comment">// step model</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Step</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> type;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;TextContent&gt; children;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int?</span> level;

  Step({<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.type, <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.children, <span class="hljs-keyword">this</span>.level});

  <span class="hljs-keyword">factory</span> Step.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) {
    <span class="hljs-keyword">var</span> childrenList = json[<span class="hljs-string">'children'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">List?</span> ?? [];
    <span class="hljs-built_in">List</span>&lt;TextContent&gt; parsedChildren = childrenList.map((child) =&gt; TextContent.fromJson(child)).toList();
    <span class="hljs-keyword">return</span> Step(
      type: json[<span class="hljs-string">'type'</span>] ?? <span class="hljs-string">''</span>,
      children: parsedChildren,
      level: json[<span class="hljs-string">'level'</span>],
    );
  }

  <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; toJson() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-string">'type'</span>: type,
      <span class="hljs-string">'children'</span>: children.map((child) =&gt; child.toJson()).toList(),
      <span class="hljs-string">'level'</span>: level,
    };
  }
}

<span class="hljs-comment">// description model</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Description</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> type;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;TextContent&gt; children;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int?</span> level;

  Description({<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.type, <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.children, <span class="hljs-keyword">this</span>.level});

  <span class="hljs-keyword">factory</span> Description.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) {
    <span class="hljs-keyword">var</span> childrenList = json[<span class="hljs-string">'children'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">List?</span> ?? [];
    <span class="hljs-built_in">List</span>&lt;TextContent&gt; parsedChildren = childrenList.map((child) =&gt; TextContent.fromJson(child)).toList();
    <span class="hljs-keyword">return</span> Description(
      type: json[<span class="hljs-string">'type'</span>] ?? <span class="hljs-string">''</span>,
      children: parsedChildren,
      level: json[<span class="hljs-string">'level'</span>],
    );
  }

  <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; toJson() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-string">'type'</span>: type,
      <span class="hljs-string">'children'</span>: children.map((child) =&gt; child.toJson()).toList(),
      <span class="hljs-string">'level'</span>: level,
    };
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TextContent</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> type;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> text;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">bool?</span> bold;

  TextContent({<span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.type, <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.text, <span class="hljs-keyword">this</span>.bold});

  <span class="hljs-keyword">factory</span> TextContent.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) {
    <span class="hljs-keyword">return</span> TextContent(
      type: json[<span class="hljs-string">'type'</span>] ?? <span class="hljs-string">''</span>,
      text: json[<span class="hljs-string">'text'</span>] ?? <span class="hljs-string">''</span>,
      bold: json[<span class="hljs-string">'bold'</span>] ?? <span class="hljs-keyword">false</span>,
    );
  }

  <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; toJson() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-string">'type'</span>: type,
      <span class="hljs-string">'text'</span>: text,
      <span class="hljs-string">'bold'</span>: bold,
    };
  }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Comment</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> content;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> author;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">DateTime</span> createdAt;

  Comment({
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.content,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.author,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.createdAt,
  });

  <span class="hljs-keyword">factory</span> Comment.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) {
    <span class="hljs-keyword">var</span> attributes = json[<span class="hljs-string">'attributes'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; ?? {};
    <span class="hljs-keyword">var</span> authorData = attributes[<span class="hljs-string">'comment_author'</span>]?[<span class="hljs-string">'data'</span>]?[<span class="hljs-string">'attributes'</span>] ?? {};
    <span class="hljs-keyword">return</span> Comment(
      content: attributes[<span class="hljs-string">'content'</span>] ?? <span class="hljs-string">'No content'</span>,
      author: authorData[<span class="hljs-string">'username'</span>] ?? <span class="hljs-string">'Unknown'</span>,
      createdAt: <span class="hljs-built_in">DateTime</span>.parse(attributes[<span class="hljs-string">'createdAt'</span>] ?? <span class="hljs-built_in">DateTime</span>.now().toString()),
    );
  }

  <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; toJson() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-string">'content'</span>: content,
      <span class="hljs-string">'author'</span>: author,
      <span class="hljs-string">'createdAt'</span>: createdAt.toIso8601String(),
    };
  }
}

<span class="hljs-comment">//recipe model</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Recipe</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> id;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> title;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;Description&gt; description;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> ingredients;
  <span class="hljs-keyword">late</span> <span class="hljs-built_in">int</span> likes;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">DateTime</span> createdAt;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">DateTime</span> updatedAt;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">DateTime</span> publishedAt;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;Step&gt; steps;
  <span class="hljs-keyword">late</span> <span class="hljs-built_in">int</span> commentCount;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;Comment&gt; comments;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> coverImageUrl;

  Recipe({
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.id,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.title,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.description,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.ingredients,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.likes,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.createdAt,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.updatedAt,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.publishedAt,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.steps,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.commentCount,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.comments,
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.coverImageUrl
  });

  <span class="hljs-keyword">factory</span> Recipe.fromJson(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; json) {
    <span class="hljs-keyword">var</span> attr = json[<span class="hljs-string">'attributes'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; ?? {};

    <span class="hljs-comment">// Parse descriptions</span>
    <span class="hljs-built_in">List</span>&lt;Description&gt; descriptionList = [];
    <span class="hljs-keyword">if</span> (attr[<span class="hljs-string">'description'</span>] != <span class="hljs-keyword">null</span> &amp;&amp; attr[<span class="hljs-string">'description'</span>] <span class="hljs-keyword">is</span> <span class="hljs-built_in">List</span>) {
      descriptionList = (attr[<span class="hljs-string">'description'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">List</span>).map((desc) =&gt; Description.fromJson(desc)).toList();
    }

    <span class="hljs-comment">// Parse steps</span>
    <span class="hljs-built_in">List</span>&lt;Step&gt; stepsList = [];
    <span class="hljs-keyword">if</span> (attr[<span class="hljs-string">'steps'</span>] != <span class="hljs-keyword">null</span> &amp;&amp; attr[<span class="hljs-string">'steps'</span>] <span class="hljs-keyword">is</span> <span class="hljs-built_in">List</span>) {
      stepsList = (attr[<span class="hljs-string">'steps'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">List</span>).map((step) =&gt; Step.fromJson(step)).toList();
    }

    <span class="hljs-comment">// Parse comments</span>
    <span class="hljs-built_in">List</span>&lt;Comment&gt; commentList = [];
    <span class="hljs-keyword">if</span> (attr[<span class="hljs-string">'comments'</span>] != <span class="hljs-keyword">null</span> &amp;&amp; attr[<span class="hljs-string">'comments'</span>][<span class="hljs-string">'data'</span>] != <span class="hljs-keyword">null</span> &amp;&amp; attr[<span class="hljs-string">'comments'</span>][<span class="hljs-string">'data'</span>] <span class="hljs-keyword">is</span> <span class="hljs-built_in">List</span>) {
      commentList = (attr[<span class="hljs-string">'comments'</span>][<span class="hljs-string">'data'</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">List</span>).map((comment) =&gt; Comment.fromJson(comment)).toList();
    }

    <span class="hljs-comment">// var attr = json['attributes'] as Map&lt;String, dynamic&gt;;</span>
    <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> baseUrl = dotenv.env[<span class="hljs-string">'BASE_URL'</span>]!;

    <span class="hljs-comment">// Ensure image URL is correctly prefixed</span>
    <span class="hljs-built_in">String</span> coverImageUrl = <span class="hljs-string">''</span>;
    <span class="hljs-keyword">if</span> (attr[<span class="hljs-string">'cover'</span>] != <span class="hljs-keyword">null</span> &amp;&amp; attr[<span class="hljs-string">'cover'</span>][<span class="hljs-string">'data'</span>] != <span class="hljs-keyword">null</span>) {
      <span class="hljs-keyword">var</span> imageUrl = attr[<span class="hljs-string">'cover'</span>][<span class="hljs-string">'data'</span>][<span class="hljs-string">'attributes'</span>][<span class="hljs-string">'url'</span>];
      coverImageUrl = imageUrl.startsWith(<span class="hljs-string">'http'</span>)
          ? imageUrl
          : baseUrl + imageUrl; 
    }

    <span class="hljs-keyword">return</span> Recipe(
        id: json[<span class="hljs-string">'id'</span>] ?? <span class="hljs-number">0</span>,
        title: attr[<span class="hljs-string">'title'</span>] ?? <span class="hljs-string">'No title'</span>,
        description: descriptionList,
        ingredients: attr[<span class="hljs-string">'ingredients'</span>] ?? <span class="hljs-string">'No ingredients'</span>,
        likes: attr[<span class="hljs-string">'likes'</span>] ?? <span class="hljs-number">0</span>,
        createdAt: <span class="hljs-built_in">DateTime</span>.tryParse(attr[<span class="hljs-string">'createdAt'</span>] ?? <span class="hljs-built_in">DateTime</span>.now().toIso8601String()) ?? <span class="hljs-built_in">DateTime</span>.now(),
        updatedAt: <span class="hljs-built_in">DateTime</span>.tryParse(attr[<span class="hljs-string">'updatedAt'</span>] ?? <span class="hljs-built_in">DateTime</span>.now().toIso8601String()) ?? <span class="hljs-built_in">DateTime</span>.now(),
        publishedAt: <span class="hljs-built_in">DateTime</span>.tryParse(attr[<span class="hljs-string">'publishedAt'</span>] ?? <span class="hljs-built_in">DateTime</span>.now().toIso8601String()) ?? <span class="hljs-built_in">DateTime</span>.now(),
        steps: stepsList,
        commentCount: commentList.length,
        comments: commentList,
        coverImageUrl: coverImageUrl
    );
  }

  <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; toJson() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-string">'id'</span>: id,
      <span class="hljs-string">'title'</span>: title,
      <span class="hljs-string">'description'</span>: description.map((desc) =&gt; desc.toJson()).toList(),
      <span class="hljs-string">'ingredients'</span>: ingredients,
      <span class="hljs-string">'likes'</span>: likes,
      <span class="hljs-string">'createdAt'</span>: createdAt.toIso8601String(),
      <span class="hljs-string">'updatedAt'</span>: updatedAt.toIso8601String(),
      <span class="hljs-string">'publishedAt'</span>: publishedAt.toIso8601String(),
      <span class="hljs-string">'steps'</span>: steps.map((step) =&gt; step.toJson()).toList(),
      <span class="hljs-string">'commentCount'</span>: commentCount,
      <span class="hljs-string">'comments'</span>: comments.map((comment) =&gt; comment.toJson()).toList(),
      <span class="hljs-string">'cover'</span>: coverImageUrl
    };
  }
}
</code></pre>
<p>Let’s go over this code piece by piece, as it’s a lot:</p>
<h3 id="heading-1-reciperequest">1. <strong>RecipeRequest</strong></h3>
<p>The <code>RecipeRequest</code> class represents the class that allows a user to request a recipe. It has three properties (<code>id</code>, <code>title</code>, and a list of <code>Description</code> objects as defined in the Strapi backend) with 2 methods:</p>
<ul>
<li><p><code>fromJson</code>: to convert JSON data into a <code>RecipeRequest</code> object, including parsing a list of descriptions.</p>
</li>
<li><p><code>toJson</code>: to convert a <code>RecipeRequest</code> object back to JSON.</p>
</li>
</ul>
<h3 id="heading-2-step">2. <strong>Step</strong></h3>
<p>Represents the cooking steps in a recipe. It contains a list of <code>Textcontent</code> objects, and each Step object has a type, level, and children as it is a richtext type. It also has two methods:</p>
<ul>
<li><p><code>fromJson</code>: to parse JSON to create a <code>Step</code> object.</p>
</li>
<li><p><code>toJson</code>: to convert a <code>Step</code> object back to JSON.</p>
</li>
</ul>
<h3 id="heading-3-description">3. <strong>Description</strong></h3>
<p>This class also contains a list of <code>TextContent</code> objects (<code>children</code>). Each <code>Description</code> object also has a <code>type</code> and an optional <code>level</code> to indicate hierarchical structure. It has two methods, too:</p>
<ul>
<li><p><code>fromJson</code>: to convert JSON into a <code>Description</code> object.</p>
</li>
<li><p><code>toJson</code>: to serialise a <code>Description</code> object to JSON.</p>
</li>
</ul>
<h3 id="heading-4-textcontent">4. <strong>TextContent</strong></h3>
<p>This class is designed to represent individual pieces of text within larger structures. Each <code>TextContent</code> object can contain a string of text (<code>text</code>), the type of text (<code>type</code>), and an optional boolean to indicate whether the text is bold (<code>bold</code>)</p>
<ul>
<li><p><code>fromJson</code>: Parses JSON into a <code>TextContent</code> object.</p>
</li>
<li><p><code>toJson</code>: Converts a <code>TextContent</code> object back to JSON.</p>
</li>
</ul>
<h3 id="heading-5-comment">5. <strong>Comment</strong></h3>
<p>As the name indicates, this represents a comment written by a use. It has three properties: the comment <code>content</code>, <code>author</code>, and <code>createdAt</code>. Like others, it also includes two methods:</p>
<ul>
<li><p><code>fromJson</code>: to extract and construct a <code>Comment</code> object from JSON, including parsing author data.</p>
</li>
<li><p><code>toJson</code>: to serializes a <code>Comment</code> object to JSON.</p>
</li>
</ul>
<h3 id="heading-6-recipe">6. <strong>Recipe</strong></h3>
<p>Finally, there is the <code>Recipe</code> class which is the main recipe object. It contains various details about a recipe, including id, title, descriptions, ingredients, likes, timestamps, steps, comment count, comment list, and a cover image URL. We have the:</p>
<ul>
<li><p><code>fromJson</code>: to build a <code>Recipe</code> object from JSON data. This includes parsing lists of descriptions, steps, and comments. It also adjusts the image URL to ensure it is absolute.</p>
</li>
<li><p><code>toJson</code>: to convert the <code>Recipe</code> object to JSON format.</p>
</li>
</ul>
<p>As you can see, each class is designed to handle specific parts of the recipe data, with <code>fromJson</code> methods to parse JSON into Dart objects and <code>toJson</code> methods to serialize Dart objects back to JSON.</p>
<h2 id="heading-create-services">Create Services</h2>
<p>Now that your environment variables are set up, you can create different services for communicating with the server. In your <code>lib/utils/server.dart</code> file, add the code below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:convert'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'dart:developer'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_dotenv/flutter_dotenv.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:http/http.dart'</span> <span class="hljs-keyword">as</span> http;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:shared_preferences/shared_preferences.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:easy_localization/easy_localization.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../models/recipe.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ApiService</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> baseUrl = dotenv.env[<span class="hljs-string">'BASE_URL'</span>]!;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> registerEndpoint = dotenv.env[<span class="hljs-string">'USERS_ENDPOINT_REG'</span>]!;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> loginEndpoint = dotenv.env[<span class="hljs-string">'USERS_ENDPOINT'</span>]!;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> accessToken = dotenv.env[<span class="hljs-string">'ACCESS_TOKEN'</span>]!;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> recipeEndpoint = dotenv.env[<span class="hljs-string">'RECIPE_ENDPOINT'</span>]!;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> commentEndpoint = dotenv.env[<span class="hljs-string">'COMMENT_ENDPOINT'</span>]!;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> requestEndpoint = dotenv.env[<span class="hljs-string">'R_REQUEST_ENDPOINT'</span>]!;

  <span class="hljs-comment">// Helper method to get headers with optional JWT token</span>
  Future&lt;<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt;&gt; _getHeaders({<span class="hljs-built_in">bool</span> includeJwt = <span class="hljs-keyword">false</span>}) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> headers = {
      <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
      <span class="hljs-string">"Authorization"</span>: <span class="hljs-string">"Bearer <span class="hljs-subst">$accessToken</span>"</span>,
    };
    <span class="hljs-keyword">if</span> (includeJwt) {
      <span class="hljs-keyword">final</span> jwt = <span class="hljs-keyword">await</span> getJwt();
      <span class="hljs-keyword">if</span> (jwt != <span class="hljs-keyword">null</span>) {
        headers[<span class="hljs-string">"Authorization"</span>] = <span class="hljs-string">"Bearer <span class="hljs-subst">$jwt</span>"</span>;
      }
    }
    <span class="hljs-keyword">return</span> headers;
  }

  <span class="hljs-comment">// Get JWT</span>
  Future&lt;<span class="hljs-built_in">String?</span>&gt; getJwt() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> prefs = <span class="hljs-keyword">await</span> SharedPreferences.getInstance();
    <span class="hljs-keyword">return</span> prefs.getString(<span class="hljs-string">'jwt'</span>);
  }

  <span class="hljs-comment">// Set JWT</span>
  Future&lt;<span class="hljs-keyword">void</span>&gt; setJwt(<span class="hljs-built_in">String</span> jwt) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> prefs = <span class="hljs-keyword">await</span> SharedPreferences.getInstance();
    <span class="hljs-keyword">await</span> prefs.setString(<span class="hljs-string">'jwt'</span>, jwt);
  }

  <span class="hljs-comment">// Remove JWT</span>
  Future&lt;<span class="hljs-keyword">void</span>&gt; removeJwt() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> prefs = <span class="hljs-keyword">await</span> SharedPreferences.getInstance();
    <span class="hljs-keyword">await</span> prefs.remove(<span class="hljs-string">'jwt'</span>);
  }

  <span class="hljs-comment">// Set User Data</span>
  Future&lt;<span class="hljs-keyword">void</span>&gt; setUserData(<span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">dynamic</span>&gt; data) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> prefs = <span class="hljs-keyword">await</span> SharedPreferences.getInstance();
    <span class="hljs-keyword">await</span> prefs.setString(<span class="hljs-string">'userId'</span>, data[<span class="hljs-string">'user'</span>][<span class="hljs-string">'id'</span>].toString());
    <span class="hljs-keyword">await</span> prefs.setString(<span class="hljs-string">'username'</span>, data[<span class="hljs-string">'user'</span>][<span class="hljs-string">'username'</span>]);
  }

  <span class="hljs-comment">// Remove User Data</span>
  Future&lt;<span class="hljs-keyword">void</span>&gt; removeUserData() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> prefs = <span class="hljs-keyword">await</span> SharedPreferences.getInstance();
    <span class="hljs-keyword">await</span> prefs.remove(<span class="hljs-string">'userId'</span>);
    <span class="hljs-keyword">await</span> prefs.remove(<span class="hljs-string">'username'</span>);
  }

  <span class="hljs-comment">// User Registration</span>
  Future&lt;http.Response&gt; register(<span class="hljs-built_in">String</span> username, <span class="hljs-built_in">String</span> email, <span class="hljs-built_in">String</span> password) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> url = <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'<span class="hljs-subst">$baseUrl</span><span class="hljs-subst">$registerEndpoint</span>'</span>);
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.post(
        url,
        headers: <span class="hljs-keyword">await</span> _getHeaders(),
        body: json.encode({
          <span class="hljs-string">"username"</span>: username,
          <span class="hljs-string">"email"</span>: email,
          <span class="hljs-string">"password"</span>: password,
        }),
      );
      <span class="hljs-keyword">return</span> response;
    } <span class="hljs-keyword">catch</span> (e) {
      log(<span class="hljs-string">"Error registering user: <span class="hljs-subst">$e</span>"</span>);
      <span class="hljs-keyword">rethrow</span>;
    }
  }

  <span class="hljs-comment">// User Login</span>
  Future&lt;http.Response&gt; login(<span class="hljs-built_in">String</span> email, <span class="hljs-built_in">String</span> password) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> url = <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'<span class="hljs-subst">$baseUrl</span><span class="hljs-subst">$loginEndpoint</span>'</span>);
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.post(
        url,
        headers: <span class="hljs-keyword">await</span> _getHeaders(),
        body: json.encode({
          <span class="hljs-string">"identifier"</span>: email,
          <span class="hljs-string">"password"</span>: password,
        }),
      );

      <span class="hljs-keyword">if</span> (response.statusCode == <span class="hljs-number">200</span>) {
        <span class="hljs-keyword">final</span> data = json.decode(response.body);
        <span class="hljs-keyword">await</span> setJwt(data[<span class="hljs-string">'jwt'</span>]);
        <span class="hljs-keyword">await</span> setUserData(data);
      }

      <span class="hljs-keyword">return</span> response;
    } <span class="hljs-keyword">catch</span> (e) {
      log(<span class="hljs-string">"Error logging in user: <span class="hljs-subst">$e</span>"</span>);
      <span class="hljs-keyword">rethrow</span>;
    }
  }

  <span class="hljs-comment">// User Logout</span>
  Future&lt;<span class="hljs-keyword">void</span>&gt; logout() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">await</span> removeJwt();
    <span class="hljs-keyword">await</span> removeUserData();
  }

  <span class="hljs-comment">// Fetch Recipes</span>
  Future&lt;<span class="hljs-built_in">List</span>&lt;Recipe&gt;&gt; fetchRecipes(BuildContext context) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> localeCode = context.locale.toString().replaceAll(<span class="hljs-string">'_'</span>, <span class="hljs-string">'-'</span>);
    <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> lang = localeCode == <span class="hljs-string">'en'</span> ? <span class="hljs-string">'en'</span> : localeCode;
    <span class="hljs-keyword">final</span> url = <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'<span class="hljs-subst">$baseUrl</span><span class="hljs-subst">$recipeEndpoint</span>?locale=<span class="hljs-subst">$lang</span>&amp;populate=*'</span>);
    <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.<span class="hljs-keyword">get</span>(url);

    <span class="hljs-keyword">if</span> (response.statusCode == <span class="hljs-number">200</span>) {
      <span class="hljs-keyword">var</span> jsonResponse = jsonDecode(response.body);
      <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">dynamic</span>&gt; dataList = jsonResponse[<span class="hljs-string">'data'</span>];
      <span class="hljs-built_in">List</span>&lt;Recipe&gt; recipes = [];

      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> item <span class="hljs-keyword">in</span> dataList) {
        <span class="hljs-keyword">try</span> {
          recipes.add(Recipe.fromJson(item));
        } <span class="hljs-keyword">catch</span> (e) {
          <span class="hljs-built_in">print</span>(<span class="hljs-string">'Failed to parse item: <span class="hljs-subst">$e</span>'</span>);
          <span class="hljs-built_in">print</span>(<span class="hljs-string">'Item data: <span class="hljs-subst">$item</span>'</span>);
        }
      }

      <span class="hljs-keyword">return</span> recipes;
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Failed to load recipes: HTTP <span class="hljs-subst">${response.statusCode}</span>'</span>);
    }
  }

  <span class="hljs-comment">// Fetch Comments</span>
    Future&lt;<span class="hljs-built_in">List</span>&lt;Comment&gt;&gt; fetchComments(<span class="hljs-built_in">int</span> recipeId) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> url = <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'<span class="hljs-subst">$baseUrl</span><span class="hljs-subst">$commentEndpoint</span>?filters[recipe][id][\$eq]=<span class="hljs-subst">$recipeId</span>&amp;populate=comment_author'</span>);
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.<span class="hljs-keyword">get</span>(url, headers: <span class="hljs-keyword">await</span> _getHeaders());
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Response fetch status: <span class="hljs-subst">${response.statusCode}</span>'</span>);
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Response fetch body: <span class="hljs-subst">${response.body}</span>'</span>);

      <span class="hljs-keyword">if</span> (response.statusCode == <span class="hljs-number">200</span>) {
        <span class="hljs-keyword">var</span> jsonData = jsonDecode(response.body);
        <span class="hljs-built_in">print</span>(<span class="hljs-string">"Parsed JSON: <span class="hljs-subst">$jsonData</span>"</span>);

        <span class="hljs-keyword">if</span> (jsonData != <span class="hljs-keyword">null</span> &amp;&amp; jsonData.containsKey(<span class="hljs-string">'data'</span>)) {
          <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">dynamic</span>&gt; data = jsonData[<span class="hljs-string">'data'</span>];
          <span class="hljs-keyword">return</span> data.map&lt;Comment&gt;((json) {
            <span class="hljs-keyword">if</span> (json == <span class="hljs-keyword">null</span> || json[<span class="hljs-string">'attributes'</span>] == <span class="hljs-keyword">null</span>) {
              <span class="hljs-built_in">print</span>(<span class="hljs-string">'json or json[\'attributes\'] is null'</span>);
              <span class="hljs-keyword">return</span> Comment(content: <span class="hljs-string">'Invalid'</span>, author: <span class="hljs-string">'Invalid'</span>, createdAt: <span class="hljs-built_in">DateTime</span>.now());
            }
            <span class="hljs-keyword">return</span> Comment.fromJson(json);
          }).toList();
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-built_in">print</span>(<span class="hljs-string">'Data field is missing or null in the response'</span>);
          <span class="hljs-keyword">return</span> [];
        }
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-built_in">print</span>(<span class="hljs-string">'Failed to load comments with status code: <span class="hljs-subst">${response.statusCode}</span>'</span>);
        <span class="hljs-keyword">return</span> [];
      }
    } <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Error server fetching comments: <span class="hljs-subst">$e</span>'</span>);
      <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Error fetching comments: <span class="hljs-subst">$e</span>'</span>);
    }
  }

  Future&lt;Comment&gt; postComment(<span class="hljs-built_in">String</span> content, <span class="hljs-built_in">int</span> recipeId, <span class="hljs-built_in">String</span> authorId) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> url = <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'<span class="hljs-subst">$baseUrl</span><span class="hljs-subst">$commentEndpoint</span>?populate=comment_author'</span>);
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.post(
        url,
        headers: <span class="hljs-keyword">await</span> _getHeaders(),
        body: json.encode({
          <span class="hljs-string">"data"</span>: {
            <span class="hljs-string">"content"</span>: content,
            <span class="hljs-string">"recipe"</span>: recipeId,
            <span class="hljs-string">"comment_author"</span>: authorId,
          },
        }),
      );
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Post comment response status: <span class="hljs-subst">${response.statusCode}</span>'</span>);
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Post comment response body: <span class="hljs-subst">${response.body}</span>'</span>);

      <span class="hljs-keyword">if</span> (response.statusCode == <span class="hljs-number">200</span> || response.statusCode == <span class="hljs-number">201</span>) {
        <span class="hljs-keyword">var</span> jsonData = jsonDecode(response.body);
        <span class="hljs-keyword">return</span> Comment.fromJson(jsonData[<span class="hljs-string">'data'</span>]);
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Failed to post comment'</span>);
      }
    } <span class="hljs-keyword">catch</span> (e) {
      log(<span class="hljs-string">"Error posting comment: <span class="hljs-subst">$e</span>"</span>);
      <span class="hljs-keyword">rethrow</span>;
    }
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; updateCommentCount(<span class="hljs-built_in">int</span> recipeId, {<span class="hljs-keyword">required</span> <span class="hljs-built_in">bool</span> increment}) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> recipeUrl = <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'<span class="hljs-subst">$baseUrl</span><span class="hljs-subst">$recipeEndpoint</span>/<span class="hljs-subst">$recipeId</span>'</span>);
    <span class="hljs-keyword">try</span> {
      <span class="hljs-comment">// Fetch the current recipe data</span>
      <span class="hljs-keyword">final</span> recipeResponse = <span class="hljs-keyword">await</span> http.<span class="hljs-keyword">get</span>(recipeUrl, headers: <span class="hljs-keyword">await</span> _getHeaders());
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Fetch recipe response status: <span class="hljs-subst">${recipeResponse.statusCode}</span>'</span>);
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Fetch recipe response body: <span class="hljs-subst">${recipeResponse.body}</span>'</span>);

      <span class="hljs-keyword">if</span> (recipeResponse.statusCode == <span class="hljs-number">200</span>) {
        <span class="hljs-keyword">var</span> recipeData = jsonDecode(recipeResponse.body)[<span class="hljs-string">'data'</span>];
        <span class="hljs-built_in">int</span> currentComments = recipeData[<span class="hljs-string">'attributes'</span>][<span class="hljs-string">'comments'</span>] ?? <span class="hljs-number">0</span>;
        <span class="hljs-built_in">int</span> updatedComments = increment ? currentComments + <span class="hljs-number">1</span> : currentComments - <span class="hljs-number">1</span>;

        <span class="hljs-comment">// Ensure updatedComments is not negative</span>
        <span class="hljs-keyword">if</span> (updatedComments &lt; <span class="hljs-number">0</span>) {
          updatedComments = <span class="hljs-number">0</span>;
        }

        <span class="hljs-comment">// Update the recipe with the new comment count</span>
        <span class="hljs-keyword">final</span> updateResponse = <span class="hljs-keyword">await</span> http.put(
          recipeUrl,
          headers: <span class="hljs-keyword">await</span> _getHeaders(),
          body: json.encode({
            <span class="hljs-string">"data"</span>: {
              <span class="hljs-string">"comments"</span>: updatedComments,
            },
          }),
        );

        <span class="hljs-built_in">print</span>(<span class="hljs-string">'Update recipe response status: <span class="hljs-subst">${updateResponse.statusCode}</span>'</span>);
        <span class="hljs-built_in">print</span>(<span class="hljs-string">'Update recipe response body: <span class="hljs-subst">${updateResponse.body}</span>'</span>);

        <span class="hljs-keyword">if</span> (updateResponse.statusCode != <span class="hljs-number">200</span>) {
          <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Failed to update comment count'</span>);
        }
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Failed to fetch recipe data'</span>);
      }
    } <span class="hljs-keyword">catch</span> (e) {
      log(<span class="hljs-string">"Error updating comment count: <span class="hljs-subst">$e</span>"</span>);
      <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Error updating comment count: <span class="hljs-subst">$e</span>'</span>);
    }
  }

  <span class="hljs-comment">// Like Recipe</span>
  Future&lt;<span class="hljs-keyword">void</span>&gt; likeRecipe(<span class="hljs-built_in">int</span> recipeId) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> recipeUrl = <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'<span class="hljs-subst">$baseUrl</span><span class="hljs-subst">$recipeEndpoint</span>/<span class="hljs-subst">$recipeId</span>'</span>);
    <span class="hljs-keyword">try</span> {
      <span class="hljs-comment">// Fetch the current recipe data</span>
      <span class="hljs-keyword">final</span> recipeResponse = <span class="hljs-keyword">await</span> http.<span class="hljs-keyword">get</span>(recipeUrl, headers: <span class="hljs-keyword">await</span> _getHeaders());
      <span class="hljs-keyword">if</span> (recipeResponse.statusCode == <span class="hljs-number">200</span>) {
        <span class="hljs-keyword">var</span> recipeData = jsonDecode(recipeResponse.body)[<span class="hljs-string">'data'</span>];
        <span class="hljs-built_in">int</span> currentLikes = recipeData[<span class="hljs-string">'attributes'</span>][<span class="hljs-string">'likes'</span>] ?? <span class="hljs-number">0</span>;
        <span class="hljs-built_in">int</span> updatedLikes = currentLikes + <span class="hljs-number">1</span>;

        <span class="hljs-comment">// Update the recipe with the new likes count</span>
        <span class="hljs-keyword">final</span> updateResponse = <span class="hljs-keyword">await</span> http.put(
          recipeUrl,
          headers: <span class="hljs-keyword">await</span> _getHeaders(),
          body: json.encode({
            <span class="hljs-string">"data"</span>: {
              <span class="hljs-string">"likes"</span>: updatedLikes,
            },
          }),
        );

        <span class="hljs-keyword">if</span> (updateResponse.statusCode != <span class="hljs-number">200</span>) {
          <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Failed to update likes count'</span>);
        }
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Failed to fetch recipe data'</span>);
      }
    } <span class="hljs-keyword">catch</span> (e) {
      log(<span class="hljs-string">"Error liking recipe: <span class="hljs-subst">$e</span>"</span>);
      <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Error liking recipe: <span class="hljs-subst">$e</span>'</span>);
    }
  }

  <span class="hljs-comment">// Submit Recipe Request</span>
  Future&lt;<span class="hljs-keyword">void</span>&gt; submitRecipeRequest(RecipeRequest r_request) <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> url = <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'<span class="hljs-subst">$baseUrl</span><span class="hljs-subst">$requestEndpoint</span>'</span>);

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.post(
        url,
        headers: <span class="hljs-keyword">await</span> _getHeaders(includeJwt: <span class="hljs-keyword">true</span>),
        body: jsonEncode({
          <span class="hljs-string">'data'</span>: r_request.toJson(), <span class="hljs-comment">// Wrap the request in a 'data' object</span>
        }),
      );
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Response status code: <span class="hljs-subst">${response.statusCode}</span>'</span>);
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Response body: <span class="hljs-subst">${response.body}</span>'</span>);
      <span class="hljs-keyword">if</span> (response.statusCode != <span class="hljs-number">200</span> &amp;&amp; response.statusCode != <span class="hljs-number">201</span>) {
        <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Failed to submit recipe request'</span>);
      }
    } <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">"Error submitting recipe request: <span class="hljs-subst">$e</span>"</span>);
      <span class="hljs-keyword">rethrow</span>;
    }
  }

  <span class="hljs-comment">// Fetch User Requested Recipes</span>
  Future&lt;<span class="hljs-built_in">List</span>&lt;RecipeRequest&gt;&gt; fetchUserRequestedRecipes() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> url = <span class="hljs-built_in">Uri</span>.parse(<span class="hljs-string">'<span class="hljs-subst">$baseUrl</span><span class="hljs-subst">$requestEndpoint</span>'</span>);
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> http.<span class="hljs-keyword">get</span>(
        url,
        headers: <span class="hljs-keyword">await</span> _getHeaders(includeJwt: <span class="hljs-keyword">true</span>),
      );
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Response status code: <span class="hljs-subst">${response.statusCode}</span>'</span>);
      <span class="hljs-built_in">print</span>(<span class="hljs-string">'Response body: <span class="hljs-subst">${response.body}</span>'</span>);

      <span class="hljs-keyword">if</span> (response.statusCode == <span class="hljs-number">200</span>) {
        <span class="hljs-keyword">var</span> jsonResponse = jsonDecode(response.body);
        <span class="hljs-built_in">List</span>&lt;<span class="hljs-built_in">dynamic</span>&gt; data = jsonResponse[<span class="hljs-string">'data'</span>];
        <span class="hljs-keyword">return</span> data.map((json) =&gt; RecipeRequest.fromJson(json)).toList();
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">throw</span> Exception(<span class="hljs-string">'Failed to load user requested recipes'</span>);
      }
    } <span class="hljs-keyword">catch</span> (e) {
      <span class="hljs-built_in">print</span>(<span class="hljs-string">"Error fetching user requested recipes: <span class="hljs-subst">$e</span>"</span>);
      <span class="hljs-keyword">rethrow</span>;
    }
  }
}
</code></pre>
<p>The <code>ApiService</code> class from the code above is a utility for handling various operations related to user authentication and data fetching from a backend server. This service uses HTTP requests to communicate with the Strapi server.</p>
<p>There are four main entities:</p>
<h3 id="heading-1-class-variables">1. Class Variables</h3>
<ul>
<li><p><code>baseUrl</code> is the base URL.</p>
</li>
<li><p><code>registerEndpoint</code>, <code>loginEndpoint</code>, <code>recipeEndpoint</code>, <code>commentEndpoint</code>, <code>requestEndpoint</code> are the specific endpoints for registration, login, recipes, comments, and requests.</p>
</li>
<li><p><code>accessToken</code> is the token used for API authentication.</p>
</li>
</ul>
<h3 id="heading-2-helper-methods">2. Helper Methods</h3>
<ul>
<li><p><code>_getHeaders</code> prepares the headers for HTTP requests and it optionally includes a JWT token if <code>includeJwt</code> is true.</p>
</li>
<li><p><code>getJwt</code> retrieves the JWT token from shared preferences.</p>
</li>
<li><p><code>setJwt</code> and <code>setUserData</code> store the JWT token and user data (ID and username) in shared preferences once the user logs in.</p>
</li>
<li><p><code>removeJwt</code> and <code>removeUserData</code> remove the JWT token and user data from shared preferences, respectively, and log the user out.</p>
</li>
</ul>
<h3 id="heading-3-user-operations">3. User Operations</h3>
<ul>
<li><p><code>register</code> registers a new user with the given username, email, and password. It sends a POST request to the registration endpoint with the user details.</p>
</li>
<li><p><code>login</code> logs in a user with the given email and password. If successful, it stores the received JWT token and user data.</p>
</li>
<li><p><code>logout</code> logs out the user by removing the JWT token and user data from shared preferences.</p>
</li>
</ul>
<h3 id="heading-4-data-fetching-and-manipulation">4. Data Fetching and Manipulation</h3>
<ul>
<li><p><code>fetchRecipes</code> fetches a list of recipes based on the current locale (language) from the backend. It handles parsing the JSON response into a list of <code>Recipe</code> objects.</p>
</li>
<li><p><code>fetchComments</code> fetches comments for a specific recipe by its ID. It populates the <code>comment_author</code> field and returns a list of <code>Comment</code> objects.</p>
</li>
<li><p><code>postComment</code> posts a new comment on a specific recipe. It sends the comment content, recipe ID, and author ID to the backend.</p>
</li>
<li><p><code>updateCommentCount</code> updates the comment count for a specific recipe. It first fetches the current count, modifies it, and then updates it on the backend.</p>
</li>
<li><p><code>likeRecipe</code>: Increments the like count for a specific recipe by fetching the current count, adding one, and updating the backend.</p>
</li>
<li><p><code>submitRecipeRequest</code> submits a new recipe request to the backend. It sends the request data wrapped in a <code>data</code> object.</p>
</li>
<li><p><code>fetchUserRequestedRecipes</code> fetches a list of recipes requested by a specific user from the backend.</p>
</li>
</ul>
<h2 id="heading-authorization-and-authentication">Authorization and Authentication</h2>
<p>Authorization is what allows a user to access a particular resource and determines if a user can perform certain actions within the application like commenting on a recipe, liking a recipe, or requesting a recipe.</p>
<p>On the other hand, authentication is the process of validating and verifying a user.</p>
<p>There are many Authorization and Authentication methods, but in this tutorial we’ll use password-based authentication and an API Key for authorization.</p>
<h3 id="heading-registration">Registration</h3>
<p>In the <code>lib/screen/signUp.dart</code> file, add the code below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:easy_localization/easy_localization.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:provider/provider.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../utils/server2.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'login.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RegisterScreen</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  _RegisterScreenState createState() =&gt; _RegisterScreenState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_RegisterScreenState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">RegisterScreen</span>&gt; </span>{
  <span class="hljs-keyword">final</span> TextEditingController usernameController = TextEditingController();
  <span class="hljs-keyword">final</span> TextEditingController emailController = TextEditingController();
  <span class="hljs-keyword">final</span> TextEditingController passwordController = TextEditingController();
  <span class="hljs-keyword">final</span> _formKey = GlobalKey&lt;FormState&gt;();
  <span class="hljs-built_in">bool</span> _isLoading = <span class="hljs-keyword">false</span>;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> dispose() {
    usernameController.dispose();
    emailController.dispose();
    passwordController.dispose();
    <span class="hljs-keyword">super</span>.dispose();
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; _register() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">if</span> (_formKey.currentState!.validate()) {
      setState(() {
        _isLoading = <span class="hljs-keyword">true</span>;
      });

      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> Provider.of&lt;ApiService&gt;(context, listen: <span class="hljs-keyword">false</span>)
          .register(usernameController.text, emailController.text, passwordController.text);

      setState(() {
        _isLoading = <span class="hljs-keyword">false</span>;
      });

      <span class="hljs-keyword">if</span> (response.statusCode == <span class="hljs-number">200</span>) {
        <span class="hljs-comment">// Navigate to the login screen after successful registration</span>
        Navigator.pushReplacement(
          context,
          MaterialPageRoute(builder: (_) =&gt; LoginScreen()),
        );
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// Handle error</span>
        showDialog(
          context: context,
          builder: (context) =&gt; AlertDialog(
            title: Text(tr(<span class="hljs-string">'register_fail'</span>)),
            content: Text(tr(<span class="hljs-string">'register_error'</span>)),
            actions: [
              TextButton(
                onPressed: () {
                  Navigator.of(context).pop();
                },
                child: Text(tr(<span class="hljs-string">'ok'</span>)),
              ),
            ],
          ),
        );
      }
    }
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(title: Text(tr(<span class="hljs-string">'register'</span>))),
      body: Padding(
        padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">16.0</span>),
        child: Form(
          key: _formKey,
          child: Column(
            children: [
              TextFormField(
                controller: usernameController,
                decoration: InputDecoration(labelText: tr(<span class="hljs-string">'username'</span>)),
                validator: (value) {
                  <span class="hljs-keyword">if</span> (value == <span class="hljs-keyword">null</span> || value.isEmpty) {
                    <span class="hljs-keyword">return</span> tr(<span class="hljs-string">'username_required'</span>);
                  }
                  <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
                },
              ),
              TextFormField(
                controller: emailController,
                decoration: InputDecoration(labelText: tr(<span class="hljs-string">'email'</span>)),
                validator: (value) {
                  <span class="hljs-keyword">if</span> (value == <span class="hljs-keyword">null</span> || value.isEmpty) {
                    <span class="hljs-keyword">return</span> tr(<span class="hljs-string">'email_required'</span>);
                  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">RegExp</span>(<span class="hljs-string">r'^[^@]+@[^@]+\.[^@]+'</span>).hasMatch(value)) {
                    <span class="hljs-keyword">return</span> tr(<span class="hljs-string">'email_invalid'</span>);
                  }
                  <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
                },
              ),
              TextFormField(
                controller: passwordController,
                decoration: InputDecoration(labelText: tr(<span class="hljs-string">'password'</span>)),
                obscureText: <span class="hljs-keyword">true</span>,
                validator: (value) {
                  <span class="hljs-keyword">if</span> (value == <span class="hljs-keyword">null</span> || value.isEmpty) {
                    <span class="hljs-keyword">return</span> tr(<span class="hljs-string">'password_required'</span>);
                  }
                  <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
                },
              ),
              SizedBox(height: <span class="hljs-number">20</span>),
              _isLoading
                  ? CircularProgressIndicator()
                  : ElevatedButton(
                onPressed: _register,
                child: Text(tr(<span class="hljs-string">'register'</span>)),
              ),
              TextButton(
                onPressed: () {
                  <span class="hljs-comment">// Navigate to the login screen</span>
                  Navigator.pushReplacement(
                    context,
                    MaterialPageRoute(builder: (_) =&gt; LoginScreen()),
                  );
                },
                child: Text(
                  tr(<span class="hljs-string">"have_account"</span>),
                  style: <span class="hljs-keyword">const</span> TextStyle(fontSize: <span class="hljs-number">16</span>),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
</code></pre>
<p>This code provides a user-friendly registration interface for the recipe application. The <code>RegisterScreen</code> class is a stateful widget that manages the registration process.</p>
<p>The <code>_register</code> method validates the form and calls the <code>register</code> method from the <code>ApiService</code>. If the registration is successful (indicated by a 200 HTTP status code), it redirects to the login screen. If it fails, an error dialog is displayed with a message.</p>
<p>The code above also employs form validation to ensure that users enter valid information. The username and password fields must not be empty, and the email field must follow a proper email format.</p>
<p>Upon submission, the form displays a loading indicator while the app communicates with the server to register the user.</p>
<p>The form's state is managed using a GlobalKey, and controllers for the text fields are properly disposed of to free up resources when the widget is removed from the tree.</p>
<h3 id="heading-login">Login</h3>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:easy_localization/easy_localization.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:provider/provider.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../utils/server2.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'signUp.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LoginScreen</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  _LoginScreenState createState() =&gt; _LoginScreenState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_LoginScreenState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">LoginScreen</span>&gt; </span>{
  <span class="hljs-keyword">final</span> TextEditingController emailController = TextEditingController();
  <span class="hljs-keyword">final</span> TextEditingController passwordController = TextEditingController();
  <span class="hljs-keyword">final</span> _formKey = GlobalKey&lt;FormState&gt;();
  <span class="hljs-built_in">bool</span> _isLoading = <span class="hljs-keyword">false</span>;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> dispose() {
    emailController.dispose();
    passwordController.dispose();
    <span class="hljs-keyword">super</span>.dispose();
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; _login() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">if</span> (_formKey.currentState!.validate()) {
      setState(() {
        _isLoading = <span class="hljs-keyword">true</span>;
      });

      <span class="hljs-keyword">final</span> response = <span class="hljs-keyword">await</span> Provider.of&lt;ApiService&gt;(context, listen: <span class="hljs-keyword">false</span>)
          .login(emailController.text, passwordController.text);

      setState(() {
        _isLoading = <span class="hljs-keyword">false</span>;
      });

      <span class="hljs-keyword">if</span> (response.statusCode == <span class="hljs-number">200</span>) {
        Navigator.pushReplacementNamed(context, <span class="hljs-string">'/home'</span>);
      } <span class="hljs-keyword">else</span> {
        showDialog(
          context: context,
          builder: (context) =&gt; AlertDialog(
            title: Text(tr(<span class="hljs-string">'login_failed'</span>)),
            content: Text(tr(<span class="hljs-string">'invalid_email_password'</span>)),
            actions: [
              TextButton(
                onPressed: () {
                  Navigator.of(context).pop();
                },
                child: Text(tr(<span class="hljs-string">'ok'</span>)),
              ),
            ],
          ),
        );
      }
    }
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(title: Text(tr(<span class="hljs-string">'login'</span>))),
      body: Padding(
        padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">16.0</span>),
        child: Form(
          key: _formKey,
          child: Column(
            children: [
              TextFormField(
                controller: emailController,
                decoration: InputDecoration(labelText: tr(<span class="hljs-string">'email'</span>)),
                validator: (value) {
                  <span class="hljs-keyword">if</span> (value == <span class="hljs-keyword">null</span> || value.isEmpty) {
                    <span class="hljs-keyword">return</span> tr(<span class="hljs-string">'email_required'</span>);
                  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">RegExp</span>(<span class="hljs-string">r'^[^@]+@[^@]+\.[^@]+'</span>).hasMatch(value)) {
                    <span class="hljs-keyword">return</span> tr(<span class="hljs-string">'email_invalid'</span>);
                  }
                  <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
                },
              ),
              TextFormField(
                controller: passwordController,
                decoration: InputDecoration(labelText: tr(<span class="hljs-string">'password'</span>)),
                obscureText: <span class="hljs-keyword">true</span>,
                validator: (value) {
                  <span class="hljs-keyword">if</span> (value == <span class="hljs-keyword">null</span> || value.isEmpty) {
                    <span class="hljs-keyword">return</span> tr(<span class="hljs-string">'password_required'</span>);
                  }
                  <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
                },
              ),
              SizedBox(height: <span class="hljs-number">20</span>),
              _isLoading
                  ? CircularProgressIndicator()
                  : ElevatedButton(
                      onPressed: _login,
                      child: Text(tr(<span class="hljs-string">'login'</span>)),
                    ),
              TextButton(
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (_) =&gt; RegisterScreen()),
                  );
                },
                child: Text(
                  tr(<span class="hljs-string">"dont_have_account"</span>),
                  style: <span class="hljs-keyword">const</span> TextStyle(fontSize: <span class="hljs-number">16</span>),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
</code></pre>
<p>The <code>LoginScreen</code> contains two input fields for the user's email and password, and it validates the inputs before attempting to log in. When the user submits the form, the app checks if the input is valid. If valid, it sets a loading indicator and sends a login request to the backend API.</p>
<p>If the login is successful, the app navigates to the home screen, whereas if the login fails, an alert dialog is displayed to inform the user of the invalid email or password. The form also uses a <code>GlobalKey</code> to manage its state and ensures that the text controllers are properly disposed of when the widget is removed from the tree.</p>
<h2 id="heading-build-app-components">Build App Components</h2>
<h3 id="heading-drawer">Drawer</h3>
<p>The Drawer is a side panel that slides in from the left (by default) and provides navigation options for the user. It’s a great way to organize your app’s sections without crowding the main screen.</p>
<p>In our app, the drawer will include links to the Request recipe screen, Profile, Logout, and languages for authenticated users.</p>
<p>In the <code>lib/components/drawer.dart</code> file, add the code below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:easy_localization/easy_localization.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:shared_preferences/shared_preferences.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../screens/profile.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../screens/requestRecipe.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CustomDrawer</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  _CustomDrawerState createState() =&gt; _CustomDrawerState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_CustomDrawerState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">CustomDrawer</span>&gt; </span>{
  <span class="hljs-built_in">bool</span> _isAuthenticated = <span class="hljs-keyword">false</span>;
  <span class="hljs-built_in">String?</span> _username;
  <span class="hljs-built_in">String?</span> _userId;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();
    _checkAuthentication();
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; _checkAuthentication() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> prefs = <span class="hljs-keyword">await</span> SharedPreferences.getInstance();
    setState(() {
      _isAuthenticated = prefs.containsKey(<span class="hljs-string">'jwt'</span>);
      _username = prefs.getString(<span class="hljs-string">'username'</span>);
      _userId = prefs.getString(<span class="hljs-string">'userId'</span>);
    });
  }

  <span class="hljs-keyword">void</span> _navigateToLogin() {
    Navigator.pushReplacementNamed(context, <span class="hljs-string">'/login'</span>);
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; _logout() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> prefs = <span class="hljs-keyword">await</span> SharedPreferences.getInstance();
    <span class="hljs-keyword">await</span> prefs.clear();
    setState(() {
      _isAuthenticated = <span class="hljs-keyword">false</span>;
      _username = <span class="hljs-keyword">null</span>;
      _userId = <span class="hljs-keyword">null</span>;
    });
    Navigator.pushReplacementNamed(context, <span class="hljs-string">'/login'</span>);
  }

  <span class="hljs-keyword">void</span> _changeLanguage(Locale locale) {
    context.setLocale(locale);
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Drawer(
      child: ListView(
        padding: EdgeInsets.zero,
        children: [
          DrawerHeader(
            decoration: BoxDecoration(
              color: Colors.blue,
            ),
            child: Text(
              _isAuthenticated ? tr(<span class="hljs-string">'hello'</span>, namedArgs: {<span class="hljs-string">'username'</span>: _username ?? <span class="hljs-string">''</span>}) : tr(<span class="hljs-string">'welcome'</span>),
              style: TextStyle(
                color: Colors.white,
                fontSize: <span class="hljs-number">24</span>,
              ),
            ),
          ),
          <span class="hljs-keyword">if</span> (_isAuthenticated)
            ListTile(
              leading: Icon(Icons.request_page),
              title:Text(tr(<span class="hljs-string">'request_recipe'</span>)),
              onTap: () {

                Navigator.push(
                  context,
                  MaterialPageRoute(builder: (context) =&gt; RecipeRequestScreen()),

                );
              },
            ),
          <span class="hljs-keyword">if</span> (_isAuthenticated)
            ListTile(
              leading: <span class="hljs-keyword">const</span> Icon(Icons.person),
              title: Text(tr(<span class="hljs-string">'profile'</span>)),
              onTap: () {
                <span class="hljs-keyword">if</span> (_userId != <span class="hljs-keyword">null</span>) {
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (context) =&gt; ProfileScreen()),
                  );
                }
              },
            ),
          <span class="hljs-keyword">if</span> (_isAuthenticated)
            ListTile(
              leading: Icon(Icons.logout),
              title: Text(tr(<span class="hljs-string">'logout'</span>)),
              onTap: _logout,

            )
          <span class="hljs-keyword">else</span>
            ListTile(
              leading: Icon(Icons.login),
              title: Text(tr(<span class="hljs-string">'login'</span>)),
              onTap: _navigateToLogin,
            ),
          Divider(),
          ListTile(
            leading: SizedBox(
              width: <span class="hljs-number">24.0</span>,
              height: <span class="hljs-number">24.0</span>,
              child: Image.asset(
                <span class="hljs-string">'assets/images/en-flag.jpg'</span>,
              ),
            ),
            title: Text(tr(<span class="hljs-string">'english'</span>)),
            onTap: () {
              Navigator.pop(context);
              _changeLanguage(Locale(<span class="hljs-string">'en'</span>));
    },
          ),
          ListTile(
            leading: SizedBox(
              width: <span class="hljs-number">24.0</span>,
              height: <span class="hljs-number">24.0</span>,
              child: Image.asset(
                <span class="hljs-string">'assets/images/fr-flag.jpg'</span>,
              ),
            ),
            title: Text(tr(<span class="hljs-string">'french'</span>)),
            onTap: () {
              Navigator.pop(context);
              _changeLanguage(Locale(<span class="hljs-string">'fr'</span>, <span class="hljs-string">'FR'</span>));
            },
          ),
          ListTile(
            leading: SizedBox(
              width: <span class="hljs-number">24.0</span>,
              height: <span class="hljs-number">24.0</span>,
              child: Image.asset(
                <span class="hljs-string">'assets/images/ja-flag.jpg'</span>,
              ),
            ),
            title: Text(tr(<span class="hljs-string">'japanese'</span>)),
            onTap: () {
              Navigator.pop(context);
              _changeLanguage(Locale(<span class="hljs-string">'ja'</span>, <span class="hljs-string">'JP'</span>));
            },
          ),
        ],
      ),
    );
  }
}
</code></pre>
<p>The <code>CustomDrawer</code> gives users access to different parts of the app and lets them switch languages. It updates its content based on the user's login status. Logged-in users see options like “Request a Recipe,” “Profile,” and “Logout,” while guests only see a “Login” option. It personalizes the user experience by greeting logged-in users with their username.</p>
<p>It also includes a language switcher with flag icons for English, French, and Japanese, powered by the <code>easy_localization</code> package. This allows users to change the app’s language instantly.</p>
<p>On startup, the drawer checks the user's authentication status using <code>SharedPreferences</code> and adjusts the UI accordingly. Navigation is handled with <code>Navigator</code>, enabling smooth transitions to different screens based on the selected menu item.</p>
<h3 id="heading-appbar">AppBar</h3>
<p>The AppBar is the top bar of your app’s screen. It typically contains the app’s title, a back button (if needed), and sometimes actions like search, settings, or a language toggle. In our multilingual recipe app, we’ll use the <code>AppBar</code> to show the current page title and allow easy navigation through the drawer.</p>
<p>In the <code>lib/components/appBar.dart</code> file, add the code below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-comment">/// <span class="markdown">A customizable AppBar for the Recipe application.</span></span>
<span class="hljs-comment">///
<span class="markdown">/// This AppBar allows for setting a title, actions, a leading widget, </span></span>
<span class="hljs-comment">/// <span class="markdown">centering the title, background color, and elevation.</span></span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RecipeBar</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">PreferredSizeWidget</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> title;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;Widget&gt;? actions;
  <span class="hljs-keyword">final</span> Widget? leading;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">bool</span> centerTitle;
  <span class="hljs-keyword">final</span> Color? backgroundColor;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">double</span> elevation;

  <span class="hljs-keyword">const</span> RecipeBar({
    <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.title,
    <span class="hljs-keyword">this</span>.actions,
    <span class="hljs-keyword">this</span>.leading,
    <span class="hljs-keyword">this</span>.centerTitle = <span class="hljs-keyword">true</span>,
    <span class="hljs-keyword">this</span>.backgroundColor,
    <span class="hljs-keyword">this</span>.elevation = <span class="hljs-number">4.0</span>,
    Key? key,
  }) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> AppBar(
      title: Text(title),
      actions: actions,
      leading: leading,
      centerTitle: centerTitle,
      backgroundColor: backgroundColor,
      elevation: elevation,
    );
  }

  <span class="hljs-meta">@override</span>
  Size <span class="hljs-keyword">get</span> preferredSize =&gt; <span class="hljs-keyword">const</span> Size.fromHeight(kToolbarHeight);
}
</code></pre>
<p>The AppBar uses a <code>StatelessWidget</code> since it does not manage any state that changes over time. It implements the <code>PreferredSizeWidget</code> interface, which is necessary for AppBar customization in Flutter.</p>
<p>The constructor of the <code>RecipeBar</code> class takes several parameters to customize the AppBar. The <code>title</code> parameter is required, while the others are optional with default values. The <code>actions</code> parameter allows adding widgets like buttons for login, language switching, or simply navigating to another screen of the app.</p>
<p>In the <code>build</code> method, the AppBar is constructed using the provided parameters. The <code>preferredSize</code> getter returns the preferred height of the AppBar, which is set to the standard toolbar height using <code>kToolbarHeight</code>. This class provides a flexible and reusable AppBar component for the Recipe application, enabling easy customization and consistent UI design across different screens.</p>
<h2 id="heading-fetch-recipes">Fetch Recipes</h2>
<p>In the <code>lib/screens/home.dart</code> file, add the code below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:easy_localization/easy_localization.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:shared_preferences/shared_preferences.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../components/drawer.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../models/recipe.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../utils/server2.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'detail.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HomeScreen</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  _HomeScreenState createState() =&gt; _HomeScreenState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_HomeScreenState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">HomeScreen</span>&gt; </span>{
  <span class="hljs-keyword">late</span> Future&lt;<span class="hljs-built_in">List</span>&lt;Recipe&gt;&gt; _recipesFuture;
  <span class="hljs-built_in">bool</span> _isAuthenticated = <span class="hljs-keyword">false</span>;
  <span class="hljs-built_in">String?</span> _username;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();
    _checkAuthentication(); <span class="hljs-comment">// Check authentication state when initializing</span>
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; _checkAuthentication() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> prefs = <span class="hljs-keyword">await</span> SharedPreferences.getInstance();
    setState(() {
      _isAuthenticated = prefs.containsKey(<span class="hljs-string">'jwt'</span>); <span class="hljs-comment">// Check if JWT token is stored</span>
      _username = prefs.getString(<span class="hljs-string">'username'</span>); <span class="hljs-comment">// Get the logged-in user's username from shared preferences</span>
    });
  }

  <span class="hljs-keyword">void</span> _navigateToLogin() {
    Navigator.pushReplacementNamed(context, <span class="hljs-string">'/login'</span>);
  }

  <span class="hljs-comment">// Logout method</span>
  Future&lt;<span class="hljs-keyword">void</span>&gt; _logout() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">await</span> ApiService().logout();
    setState(() {
      _isAuthenticated = <span class="hljs-keyword">false</span>;
      _username = <span class="hljs-keyword">null</span>;
    });
    Navigator.pushReplacementNamed(context, <span class="hljs-string">'/login'</span>);
  }

  <span class="hljs-built_in">String</span> truncateWithEllipsis(<span class="hljs-built_in">int</span> cutoff, <span class="hljs-built_in">String</span> myString) {
    <span class="hljs-keyword">return</span> (myString.length &lt;= cutoff) ? myString : <span class="hljs-string">'<span class="hljs-subst">${myString.substring(<span class="hljs-number">0</span>, cutoff)}</span>...'</span>;
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> didChangeDependencies() {
    <span class="hljs-keyword">super</span>.didChangeDependencies();
    <span class="hljs-comment">// Initialize _recipesFuture  after context is available</span>
    _recipesFuture = ApiService().fetchRecipes(context);
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: Text(tr(<span class="hljs-string">'recipe_list'</span>)),
        actions: [
          <span class="hljs-keyword">if</span> (_isAuthenticated)
            Padding(
              padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">8.0</span>),
              child: Center(
                child: Text(tr(<span class="hljs-string">'hello'</span>, namedArgs: {<span class="hljs-string">'username'</span>: _username ?? <span class="hljs-string">''</span>})),
              ),
            ),
          <span class="hljs-keyword">if</span> (_isAuthenticated)
            IconButton(
              icon: <span class="hljs-keyword">const</span> Icon(Icons.logout),
              onPressed: _logout,
            )
          <span class="hljs-keyword">else</span>
            TextButton(
              onPressed: _navigateToLogin,
              child: Text(
                tr(<span class="hljs-string">'login'</span>),
                style: <span class="hljs-keyword">const</span> TextStyle(color: Colors.white),
              ),
            ),
        ],
      ),
      drawer: CustomDrawer(),
      body: FutureBuilder&lt;<span class="hljs-built_in">List</span>&lt;Recipe&gt;&gt;(
        future: _recipesFuture,
        builder: (context, snapshot) {
          <span class="hljs-keyword">if</span> (snapshot.connectionState == ConnectionState.waiting) {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">const</span> Center(child: CircularProgressIndicator());
          } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (snapshot.hasError) {
            <span class="hljs-keyword">return</span> Center(child: Text(<span class="hljs-string">'Error: <span class="hljs-subst">${snapshot.error.toString()}</span>'</span>));
          } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (snapshot.data == <span class="hljs-keyword">null</span> || snapshot.data!.isEmpty) {
            <span class="hljs-keyword">return</span> Center(child: Text(tr(<span class="hljs-string">'no_recipe'</span>)));
          }

          <span class="hljs-keyword">return</span> ListView.builder(
            itemCount: snapshot.data!.length,
            itemBuilder: (context, index) {
              Recipe recipe = snapshot.data![index];
              <span class="hljs-built_in">String</span> fullDescription = recipe.description.isNotEmpty
                  ? recipe.description.map((d) =&gt; d.children.map((t) =&gt; t.text).join(<span class="hljs-string">' '</span>)).join(<span class="hljs-string">'\n'</span>)
                  : tr(<span class="hljs-string">'no_description'</span>);
              <span class="hljs-built_in">String</span> truncatedDescription = truncateWithEllipsis(<span class="hljs-number">100</span>, fullDescription);

              <span class="hljs-built_in">print</span>(<span class="hljs-string">"Recipe Title: <span class="hljs-subst">${recipe.title}</span>"</span>);
              <span class="hljs-built_in">print</span>(<span class="hljs-string">"Full Description: <span class="hljs-subst">$fullDescription</span>"</span>);

              <span class="hljs-keyword">return</span> GestureDetector(
                onTap: () <span class="hljs-keyword">async</span> {
                  <span class="hljs-keyword">final</span> result = <span class="hljs-keyword">await</span> Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) =&gt; RecipeDetailPage(recipe: recipe),
                    ),
                  );

                  <span class="hljs-keyword">if</span> (result != <span class="hljs-keyword">null</span> &amp;&amp; result <span class="hljs-keyword">is</span> <span class="hljs-built_in">Map</span>&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">int</span>&gt;) {
                    setState(() {
                      Recipe updatedRecipe = Recipe(
                        id: recipe.id,
                        title: recipe.title,
                        description: recipe.description,
                        ingredients: recipe.ingredients,
                        likes: result[<span class="hljs-string">'likes'</span>]!,
                        createdAt: recipe.createdAt,
                        updatedAt: recipe.updatedAt,
                        publishedAt: recipe.publishedAt,
                        steps: recipe.steps,
                        commentCount: result[<span class="hljs-string">'commentsCount'</span>]!,
                        comments: recipe.comments,
                        coverImageUrl: recipe.coverImageUrl,
                      );
                      snapshot.data![index] = updatedRecipe;
                    });
                  }
                },
                child: Container(
                  margin: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(horizontal: <span class="hljs-number">10</span>, vertical: <span class="hljs-number">8</span>),
                  padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">10</span>),
                  decoration: BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.circular(<span class="hljs-number">15</span>),
                    border: Border.all(
                      color: <span class="hljs-keyword">const</span> Color(<span class="hljs-number">0xff595959</span>),
                      width: <span class="hljs-number">0.5</span>,
                    ),
                  ),
                  child: Row(
                    children: [
                      Container(
                        height: <span class="hljs-number">80</span>,
                        width: <span class="hljs-number">80</span>,
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(<span class="hljs-number">15</span>),
                          image: DecorationImage(
                            image: NetworkImage(recipe.coverImageUrl),
                            fit: BoxFit.cover,
                          ),
                        ),
                      ),
                      <span class="hljs-keyword">const</span> SizedBox(width: <span class="hljs-number">10</span>),
                      Expanded(
                        flex: <span class="hljs-number">3</span>,
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(
                              recipe.title.toUpperCase(),
                              style: <span class="hljs-keyword">const</span> TextStyle(fontWeight: FontWeight.bold),
                            ),
                            <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">5</span>),
                            Text(
                              truncatedDescription,
                              style: <span class="hljs-keyword">const</span> TextStyle(color: Color(<span class="hljs-number">0xff595959</span>)),
                            ),
                            <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">5</span>),
                            Row(
                              children: [
                                Expanded(
                                  child: Row(
                                    children: [
                                      Text(<span class="hljs-string">'<span class="hljs-subst">${recipe.likes}</span>'</span>),
                                      <span class="hljs-keyword">const</span> SizedBox(width: <span class="hljs-number">5</span>),
                                      <span class="hljs-keyword">const</span> Icon(Icons.thumb_up, size: <span class="hljs-number">18</span>, color: Colors.redAccent),
                                    ],
                                  ),
                                ),
                                Expanded(
                                  child: Row(
                                    children: [
                                      Text(<span class="hljs-string">'<span class="hljs-subst">${recipe.commentCount}</span>'</span>),
                                      <span class="hljs-keyword">const</span> SizedBox(width: <span class="hljs-number">5</span>),
                                      <span class="hljs-keyword">const</span> Icon(Icons.comment, size: <span class="hljs-number">18</span>, color: Colors.blue),
                                    ],
                                  ),
                                ),
                              ],
                            ),
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
              );
            },
          );
        },
      ),
    );
  }
}
</code></pre>
<p>The <code>HomeScreen</code> mainly displays a list of recipes. It checks if the user is authenticated by looking for a JWT token in shared preferences and sets the authentication state accordingly. If the user is authenticated, it shows a greeting with their username and provides a logout option in the app bar.</p>
<p>The <code>FutureBuilder</code> to fetch recipes from the <code>ApiService</code>. While the data is being fetched, it shows a loading indicator. Once the data is fetched, it displays the list of recipes. Each recipe card includes the title, truncated description, cover image, and the counts of likes and comments.</p>
<p>When a user taps on a recipe, it navigates to a detailed page for that recipe. If the detailed page updates the recipe's likes or comments, the list updates accordingly without reloading the entire screen.</p>
<h2 id="heading-view-recipe">View Recipe</h2>
<p>In the <code>lib/screens/detail.dart</code> file, add the code below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:developer'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:easy_localization/easy_localization.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:shared_preferences/shared_preferences.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../models/recipe.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../utils/server2.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RecipeDetailPage</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-keyword">final</span> Recipe recipe;

  <span class="hljs-keyword">const</span> RecipeDetailPage({Key? key, <span class="hljs-keyword">required</span> <span class="hljs-keyword">this</span>.recipe}) : <span class="hljs-keyword">super</span>(key: key);

  <span class="hljs-meta">@override</span>
  _RecipeDetailPageState createState() =&gt; _RecipeDetailPageState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_RecipeDetailPageState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">RecipeDetailPage</span>&gt; </span>{
  <span class="hljs-keyword">final</span> _commentController = TextEditingController();
  <span class="hljs-built_in">List</span>&lt;Comment&gt; _comments = [];
  <span class="hljs-built_in">bool</span> _isLoading = <span class="hljs-keyword">true</span>;
  <span class="hljs-built_in">bool</span> _isAuthenticated = <span class="hljs-keyword">false</span>;
  <span class="hljs-built_in">String?</span> _userId;
  <span class="hljs-built_in">int</span> _likes = <span class="hljs-number">0</span>;
  <span class="hljs-built_in">int</span> _commentsCount = <span class="hljs-number">0</span>;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();
    _initializePage();
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; _initializePage() <span class="hljs-keyword">async</span> {
    _checkAuthentication();
    _loadComments();
    _likes = widget.recipe.likes;
    _comments = widget.recipe.comments;
    _commentsCount = widget.recipe.commentCount;
    _commentController.addListener(() =&gt; setState(() {}));
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> dispose() {
    _commentController.dispose();
    <span class="hljs-keyword">super</span>.dispose();
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; _checkAuthentication() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">final</span> prefs = <span class="hljs-keyword">await</span> SharedPreferences.getInstance();
    setState(() {
      _isAuthenticated = prefs.containsKey(<span class="hljs-string">'jwt'</span>);
      _userId = prefs.getString(<span class="hljs-string">'userId'</span>);
    });
  }

  <span class="hljs-keyword">void</span> _showError(<span class="hljs-built_in">String</span> message) {
    <span class="hljs-keyword">final</span> snackBar = SnackBar(content: Text(message));
    ScaffoldMessenger.of(context).showSnackBar(snackBar);
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; _loadComments() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">var</span> comments = <span class="hljs-keyword">await</span> ApiService().fetchComments(widget.recipe.id);
      setState(() {
        _comments = comments;
        _commentsCount = comments.length;
        _isLoading = <span class="hljs-keyword">false</span>;
      });
    } <span class="hljs-keyword">catch</span> (e) {
      log(<span class="hljs-string">'Error server fetching comments: <span class="hljs-subst">$e</span>'</span>);
      _showError(<span class="hljs-string">'Failed to load comments: <span class="hljs-subst">$e</span>'</span>);
      setState(() =&gt; _isLoading = <span class="hljs-keyword">false</span>);
    }
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; _addComment() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">if</span> (_commentController.text.isNotEmpty &amp;&amp; _userId != <span class="hljs-keyword">null</span>) {
      <span class="hljs-keyword">try</span> {
        Comment newComment = <span class="hljs-keyword">await</span> ApiService().postComment(
            _commentController.text, widget.recipe.id, _userId!);

        setState(() {
          _comments.add(newComment);
          _commentsCount++;
          _commentController.clear();
        });

        <span class="hljs-keyword">await</span> ApiService().updateCommentCount(widget.recipe.id, increment: <span class="hljs-keyword">true</span>);
      } <span class="hljs-keyword">catch</span> (e) {
        log(<span class="hljs-string">"Error posting comment: <span class="hljs-subst">$e</span>"</span>);
        _showError(<span class="hljs-string">'Error posting comment: <span class="hljs-subst">$e</span>'</span>);
      }
    }
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; _likeRecipe() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> ApiService().likeRecipe(widget.recipe.id);
      setState(() =&gt; _likes++);
    } <span class="hljs-keyword">catch</span> (e) {
      log(<span class="hljs-string">"Error liking recipe: <span class="hljs-subst">$e</span>"</span>);
      _showError(<span class="hljs-string">'Error liking recipe: <span class="hljs-subst">$e</span>'</span>);
    }
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; _logout() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">await</span> ApiService().logout();
    setState(() {
      _isAuthenticated = <span class="hljs-keyword">false</span>;
      _userId = <span class="hljs-keyword">null</span>;
    });
    Navigator.pushReplacementNamed(context, <span class="hljs-string">'/login'</span>);
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> WillPopScope(
      onWillPop: () <span class="hljs-keyword">async</span> {
        Navigator.pop(context, {
          <span class="hljs-string">'likes'</span>: _likes,
          <span class="hljs-string">'commentsCount'</span>: _commentsCount,
        });
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text(widget.recipe.title),
          actions: [
            <span class="hljs-keyword">if</span> (_isAuthenticated)
              IconButton(
                icon: <span class="hljs-keyword">const</span> Icon(Icons.logout),
                onPressed: _logout,
              ),
          ],
        ),
        body: SingleChildScrollView(
          child: Padding(
            padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">8.0</span>),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                <span class="hljs-keyword">if</span> (widget.recipe.coverImageUrl.isNotEmpty)
                  Image.network(
                    widget.recipe.coverImageUrl,
                    width: <span class="hljs-built_in">double</span>.infinity,
                    height: <span class="hljs-number">200</span>,
                    fit: BoxFit.cover,
                  ),
                <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">10</span>),
                Row(
                  children: [
                    Expanded(
                      child: Row(
                        children: [
                          Text(<span class="hljs-string">'<span class="hljs-subst">$_likes</span>'</span>),
                          <span class="hljs-keyword">const</span> SizedBox(width: <span class="hljs-number">5</span>),
                          IconButton(
                            icon: <span class="hljs-keyword">const</span> Icon(Icons.thumb_up, size: <span class="hljs-number">18</span>, color: Colors.redAccent),
                            onPressed: _likeRecipe,
                          ),
                        ],
                      ),
                    ),
                    Expanded(
                      child: Row(
                        children: [
                          Text(<span class="hljs-string">'<span class="hljs-subst">$_commentsCount</span>'</span>),
                          <span class="hljs-keyword">const</span> SizedBox(width: <span class="hljs-number">5</span>),
                          <span class="hljs-keyword">const</span> Icon(Icons.comment, size: <span class="hljs-number">18</span>, color: Colors.blue),
                        ],
                      ),
                    ),
                  ],
                ),
                <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">20</span>),
                ...widget.recipe.description.map((desc) =&gt;
                    Text(desc.children.map((child) =&gt; child.text).join())),
                <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">20</span>),
                <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Ingredients'</span>, style: TextStyle(fontWeight: FontWeight.bold)),
                <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">20</span>),
                Text(widget.recipe.ingredients),
                <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">20</span>),
                <span class="hljs-keyword">const</span> Text(<span class="hljs-string">'Procedure'</span>, style: TextStyle(fontWeight: FontWeight.bold)),
                <span class="hljs-keyword">const</span> SizedBox(height: <span class="hljs-number">20</span>),
                ...widget.recipe.steps.map((step) =&gt;
                    Text(step.children.map((child) =&gt; child.text).join())),
                <span class="hljs-keyword">if</span> (_isLoading)
                  <span class="hljs-keyword">const</span> CircularProgressIndicator(),
                ..._comments.map((comment) =&gt; ListTile(
                  title: Text(comment.author),
                  subtitle: Text(comment.content),
                  trailing: Text(comment.createdAt.toLocal().toString()),
                )),
                <span class="hljs-keyword">if</span> (_isAuthenticated)
                  Column(
                    children: [
                      TextField(
                        controller: _commentController,
                        decoration: InputDecoration(labelText: tr(<span class="hljs-string">'add_comment'</span>)),
                      ),
                      ElevatedButton(
                        onPressed: _commentController.text.isNotEmpty ? _addComment : <span class="hljs-keyword">null</span>,
                        child: Text(tr(<span class="hljs-string">'submit'</span>)),
                      ),
                    ],
                  )
                <span class="hljs-keyword">else</span>
                  Text(tr(<span class="hljs-string">'login_comment'</span>)),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
</code></pre>
<p>This <code>RecipeDetailPage</code> displays detailed information about a selected recipe, including its cover image, likes, comments, ingredients, and procedure. Only authenticated users can comment or like a recipe. During initialization, the page checks if the user is authenticated by reading from local storage. If authenticated, it sets <code>_isAuthenticated</code> to <code>true</code> and retrieves the user's ID, enabling features like adding comments and liking recipes.</p>
<ul>
<li><p><strong>Adding a comment</strong>: The <code>_addComment</code> function posts the new comment to the server, adds it to the local comments list, increments the comment count, and clears the input field.</p>
</li>
<li><p><strong>Liking a recipe</strong>: The <code>_likeRecipe</code> function sends a like request to the server, increases the local like count, and updates the UI.</p>
</li>
</ul>
<p>If the user is not authenticated, they are prompted to log in to leave a comment or interact with the recipe.</p>
<h2 id="heading-create-request-recipe-screen">Create Request Recipe Screen</h2>
<p>In the <code>lib/screens/requestRecipe.dart</code> file, add the code below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:easy_localization/easy_localization.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../models/recipe.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../utils/server2.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RecipeRequestScreen</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  _RecipeRequestScreenState createState() =&gt; _RecipeRequestScreenState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_RecipeRequestScreenState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">RecipeRequestScreen</span>&gt; </span>{
  <span class="hljs-keyword">final</span> _formKey = GlobalKey&lt;FormState&gt;();
  <span class="hljs-keyword">final</span> _titleController = TextEditingController();
  <span class="hljs-keyword">final</span> _descriptionController = TextEditingController();
  <span class="hljs-keyword">final</span> ApiService _apiService = ApiService();

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> dispose() {
    _titleController.dispose();
    _descriptionController.dispose();
    <span class="hljs-keyword">super</span>.dispose();
  }

  Future&lt;<span class="hljs-keyword">void</span>&gt; _submitRequest() <span class="hljs-keyword">async</span> {
    <span class="hljs-keyword">if</span> (_formKey.currentState!.validate()) {
      <span class="hljs-keyword">final</span> description = _descriptionController.text;
      <span class="hljs-keyword">final</span> descriptionList = [
        Description(
          type: <span class="hljs-string">'paragraph'</span>,
          children: [
            TextContent(
              type: <span class="hljs-string">'text'</span>,
              text: description,
              bold: <span class="hljs-keyword">false</span>
            ),
          ],
        ),
      ];
      <span class="hljs-keyword">final</span> request = RecipeRequest(
        title: _titleController.text,
        description: descriptionList,
        id: <span class="hljs-number">0</span>,
      );
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">await</span> _apiService.submitRecipeRequest(request);
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text(tr(<span class="hljs-string">'request_successful'</span>))),
        );
        _titleController.clear();
        _descriptionController.clear();
      } <span class="hljs-keyword">catch</span> (e) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text(<span class="hljs-string">'Failed to submit recipe request: <span class="hljs-subst">$e</span>'</span>)),
        );
      }
    }
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: Text(tr(<span class="hljs-string">'request_recipe'</span>)),
      ),
      body: Padding(
        padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">16.0</span>),
        child: Form(
          key: _formKey,
          child: Column(
            children: [
              TextFormField(
                controller: _titleController,
                decoration: InputDecoration(labelText: tr(<span class="hljs-string">'recipe_title'</span>)),
                validator: (value) {
                  <span class="hljs-keyword">if</span> (value == <span class="hljs-keyword">null</span> || value.isEmpty) {
                    <span class="hljs-keyword">return</span> <span class="hljs-string">'Please enter a title'</span>;
                  }
                  <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
                },
              ),
              TextFormField(
                controller: _descriptionController,
                decoration: InputDecoration(labelText: tr(<span class="hljs-string">'description'</span>)),
                maxLines: <span class="hljs-number">5</span>,
                validator: (value) {
                  <span class="hljs-keyword">if</span> (value == <span class="hljs-keyword">null</span> || value.isEmpty) {
                    <span class="hljs-keyword">return</span> tr(<span class="hljs-string">'enter_description'</span>);
                  }
                  <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
                },
              ),
              SizedBox(height: <span class="hljs-number">20</span>),
              ElevatedButton(
                onPressed: _submitRequest,
                child: Text(tr(<span class="hljs-string">'submit_request'</span>)),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
</code></pre>
<p>The <code>RecipeRequestPage</code> allows authenticated users to submit a request for a new recipe. <code>widget</code> is a statefull widget managed by the <code>_RecipeRequestPageState</code> class. It uses a form with two input fields: one for the recipe title and one for the description. These input fields are controlled by <code>TextEditingController</code> instances, which manage the text entered by the user.</p>
<p>The <code>_submitRequest</code> method handles the form submission. It validates the form fields, constructs a <code>RecipeRequest</code> object with the entered title and description, and sends it to the server using the <code>ApiService</code>. If the submission is successful, a success message is displayed using <code>ScaffoldMessenger</code>. If there is an error, an error message is shown.</p>
<p>The <code>build</code> method constructs the user interface of the screen and displays the form with its inputs.</p>
<h2 id="heading-create-user-profile-screen">Create User Profile Screen</h2>
<p>In the <code>lib/screens/profile.dart</code> file, add the code below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:easy_localization/easy_localization.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter_recipe_app/screens/requestRecipe.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../models/recipe.dart'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'../utils/server2.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProfileScreen</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  _ProfileScreenState createState() =&gt; _ProfileScreenState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_ProfileScreenState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">ProfileScreen</span>&gt; </span>{
  <span class="hljs-keyword">late</span> Future&lt;<span class="hljs-built_in">List</span>&lt;RecipeRequest&gt;&gt; _requestedRecipesFuture;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();
    _requestedRecipesFuture = ApiService().fetchUserRequestedRecipes();
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(
        title: Text(tr(<span class="hljs-string">'profile'</span>)),
      ),
      body: Column(
        children: [
          Padding(
            padding: <span class="hljs-keyword">const</span> EdgeInsets.all(<span class="hljs-number">16.0</span>),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                SizedBox(height: <span class="hljs-number">10</span>),
                Text(
                  tr(<span class="hljs-string">'request_list'</span>),
                  style: TextStyle(fontSize: <span class="hljs-number">16</span>, color: Colors.grey[<span class="hljs-number">600</span>]),
                ),
                SizedBox(height: <span class="hljs-number">20</span>),
                ElevatedButton(
                  onPressed: () {
                    Navigator.pop(context);
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder: (context) =&gt; RecipeRequestScreen(),
                      ),
                    );
                  },
                  child: Text(tr(<span class="hljs-string">'request_new_recipe'</span>)),
                ),
              ],
            ),
          ),
          Expanded(
            child: FutureBuilder&lt;<span class="hljs-built_in">List</span>&lt;RecipeRequest&gt;&gt;(
              future: _requestedRecipesFuture,
              builder: (context, snapshot) {
                <span class="hljs-keyword">if</span> (snapshot.connectionState == ConnectionState.waiting) {
                  <span class="hljs-keyword">return</span> Center(child: CircularProgressIndicator());
                } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (snapshot.hasError) {
                  <span class="hljs-keyword">return</span> Center(child: Text(<span class="hljs-string">'Error: <span class="hljs-subst">${snapshot.error.toString()}</span>'</span>));
                } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (snapshot.data == <span class="hljs-keyword">null</span> || snapshot.data!.isEmpty) {
                  <span class="hljs-keyword">return</span> Center(child: Text(tr(<span class="hljs-string">'no_request_found'</span>)));
                }

                <span class="hljs-keyword">return</span> ListView.builder(
                  itemCount: snapshot.data!.length,
                  itemBuilder: (context, index) {
                    RecipeRequest request = snapshot.data![index];
                    <span class="hljs-built_in">String</span> fullDescription = request.description
                        .map((d) =&gt; d.children.map((t) =&gt; t.text).join(<span class="hljs-string">'\n'</span>))
                        .join(<span class="hljs-string">'\n\n'</span>);

                    <span class="hljs-keyword">return</span> Padding(
                      padding: <span class="hljs-keyword">const</span> EdgeInsets.symmetric(horizontal: <span class="hljs-number">40.0</span>),
                      child: ListTile(
                        title: Text(
                          request.title.toUpperCase(),
                          style: <span class="hljs-keyword">const</span> TextStyle(fontWeight: FontWeight.bold),
                        ),
                        subtitle: Text(fullDescription),
                      ),
                    );
                  },
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}
</code></pre>
<p>The <code>ProfileScreen</code> class in this Flutter application represents a user's profile page where they can view their requested recipes. When the screen is initialized, it fetches a list of recipes requested by the user by calling the <code>fetchUserRequestedRecipes</code> method from the <code>ApiService</code>. This data is then stored in the <code>_requestedRecipesFuture</code> variable, which is a <code>Future</code> that will eventually hold the list of requested recipes.</p>
<p>In the <code>build</code> method, the screen is constructed using a <code>Scaffold</code> widget.</p>
<p>The main part of the screen is an <code>Expanded</code> widget containing a <code>FutureBuilder</code>. The <code>FutureBuilder</code> widget waits for the <code>_requestedRecipesFuture</code> to complete and then builds the list of requested recipes. If the data is still loading, it shows a <code>CircularProgressIndicator</code>. If there's an error, it displays an error message. And if there are no recipes, it shows a "no request found" message. Otherwise, it displays the list of requested recipes, each rendered as a <code>ListTile</code> with the recipe title and description.</p>
<h2 id="heading-test-the-app">Test the App</h2>
<p>To test the application, connect your device or launch an emulator then run the backend with the command below:</p>
<pre><code class="lang-bash">npm run develop
</code></pre>
<p>And the frontend:</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you built a Flutter and Strapi recipe application where user could register and login to request a recipe from the admin, view and like recipes, or add their comments to a specific recipe.</p>
<p>To improve the application, you can add search functionality, share functionality, or allow users not only to request a recipe but also to create a personal list of recipes they can share with others.</p>
<p>Thanks for reading!</p>
<h3 id="heading-references">References</h3>
<ul>
<li><p>⁠<a target="_blank" href="https://docs.strapi.io/dev-docs/configurations/api-tokens">https://docs.strapi.io/dev-docs/configurations/api-tokens</a></p>
</li>
<li><p>⁠⁠<a target="_blank" href="https://docs.strapi.io/user-docs/settings/API-tokens">https://docs.strapi.io/user-docs/settings/API-tokens</a></p>
</li>
<li><p>⁠⁠<a target="_blank" href="https://docs.strapi.io/dev-docs/backend-customization/examples/authentication">https://docs.strapi.io/dev-docs/backend-customization/examples/authentication</a></p>
</li>
<li><p><a target="_blank" href="https://docs.strapi.io/dev-docs/plugins/i18n">https://docs.strapi.io/dev-docs/plugins/i18n</a></p>
</li>
<li><p>⁠⁠<a target="_blank" href="https://strapi.io/blog/how-to-create-a-refresh-token-feature-in-your-strapi-application">⁠⁠https://strapi.io/blog/how-to-create-a-refresh-token-feature-in-your-strapi-application</a></p>
</li>
<li><p><a target="_blank" href="https://strapi.io/blog/a-beginners-guide-to-authentication-and-authorization-in-strapi">https://strapi.io/blog/a-beginners-guide-to-authentication-and-authorization-in-strapi</a></p>
</li>
<li><p><a target="_blank" href="https://jwt.io/introduction">https://jwt.io/introduction</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ An Animated Introduction to Programming in C++ ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, I’ll give you a comprehensive introduction to programming in C++. You don't need to have any previous programming experience in order to begin. Along the way, you will learn about the flow of control, variables, conditional statemen... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-programming-in-cpp/</link>
                <guid isPermaLink="false">67e48bff04e3150220f40379</guid>
                
                    <category>
                        <![CDATA[ C++ ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mark Mahoney ]]>
                </dc:creator>
                <pubDate>Wed, 26 Mar 2025 23:21:35 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1743028744653/12f33ee5-4ef4-47da-b50d-060a9ee327ce.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, I’ll give you a comprehensive introduction to programming in C++. You don't need to have any previous programming experience in order to begin.</p>
<p>Along the way, you will learn about the flow of control, variables, conditional statements, loops, arrays, functions, structured data, pointers and dynamic memory, classes, common data structures, and working with databases.</p>
<p>Integrated Development Environments (IDE’s) are tools that allow you to write, run, and debug C++ programs. There are some great free C++ IDE’s out there.</p>
<p>If you own a Windows computer, I recommend using <a target="_blank" href="https://visualstudio.microsoft.com/vs/community">Visual Studio Community Edition</a>. If you own a Mac, I recommend using <a target="_blank" href="https://developer.apple.com/xcode">Xcode</a>. These provide robust features and excellent support for C++ development. If you're looking for a lightweight install on any platform, <a target="_blank" href="https://www.jetbrains.com/clion/download">CLion</a> is a fantastic choice.</p>
<p>For those who prefer not to install any software or are unable to, <a target="_blank" href="https://replit.com">replit</a> is a convenient web-based IDE that allows you to start coding immediately.</p>
<p>There are practice problems in each section so that you can use to practice while learning from the content. These are in the '<strong>Hands-On Practice</strong>' portion of each section. You will use your IDE to write and run these practice programs.</p>
<h2 id="heading-code-playbacks"><strong>Code Playbacks</strong></h2>
<p>This is not a traditional online tutorial or video series. Each section will have links to interactive ‘<strong>code playbacks’</strong> that visually animate the changes made to a program in a step-by-step manner.</p>
<p>A code playback shows how a program evolves by replaying all the steps in its development. It has an author-supplied narrative, screenshots, whiteboard-style drawings, and self-grading multiple choice questions to make the learning process more dynamic and interactive. Just click on the numbered comments on the left hand side of the screen to drive the playback forward.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742956414812/64ed10ba-5f80-442b-b469-9d6462521578.png" alt="Screenshot showing what the playbacks look like" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Watch this short <a target="_blank" href="https://youtu.be/uYbHqCNjVDM">YouTube video</a> which explains how to view a code playback in more detail.</p>
<h2 id="heading-playback-press"><strong>Playback Press</strong></h2>
<p><a target="_blank" href="https://playbackpress.com/books">Playback Press</a> is a platform for sharing code playbacks. The site contains collections of code playbacks grouped together by language/technology into different ‘books’. If you want to see the full C++ book, you can go here: <a target="_blank" href="https://playbackpress.com/books/cppbook">An Animated Introduction to Programming in C++</a>.</p>
<p>Code playbacks on the platform include AI tutoring, mini-quizzes, and text-to-speech features.</p>
<p><a target="_blank" href="https://markm208.github.io/">Storyteller</a> is the free and open-source tool that powers code playbacks.</p>
<h2 id="heading-ai-tutor"><strong>AI Tutor</strong></h2>
<p>When viewing a code playback, you can ask an AI tutor about the code. It answers questions clearly and patiently, making it a helpful resource for learners. You can also ask the AI tutor to generate new self-grading multiple choice questions to test your knowledge of what you are learning.</p>
<p>In order to access the AI tutor and to generate new multiple choice questions, simply create a free account on Playback Press and add the <a target="_blank" href="https://playbackpress.com/books/cppbook">book</a> to your bookshelf. It is still free, but you do need to register in order to access the AI features.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-part-1-variables">Part 1: Variables</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-part-2-selection">Part 2: Selection</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-part-3-looping">Part 3: Looping</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-part-4-arrays">Part 4: Arrays</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-part-5-functions">Part 5: Functions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-part-6-vectors">Part 6: Vectors</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-part-7-structured-data">Part 7: Structured Data</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-part-8-pointers">Part 8: Pointers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-part-9-object-oriented-programming">Part 9: Object-Oriented Programming</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-part-10-data-structures">Part 10: Data Structures</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-part-11-sqlite-databases">Part 11: SQLite Databases</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-comments-and-feedback">Comments and Feedback</a></p>
</li>
</ul>
<h3 id="heading-c-overview">C++ Overview</h3>
<p>C++ is a powerful, high-performance programming language that is widely used in various domains such as system development, game development, real-time simulations, and high-performance applications. It is an extension of the C programming language, adding object-oriented features, which makes it suitable for large-scale software engineering projects.</p>
<p>C++ provides fine-grained control over system resources and memory management, which can lead to highly optimized and efficient code. C++ remains a popular choice due to its versatility, performance, and the vast ecosystem of libraries and tools available.</p>
<h2 id="heading-part-1-variables"><strong>Part 1: Variables</strong></h2>
<p>Simply watching an experienced artist paint is not enough to say that you have learned how to become a painter. Watching an experienced artist is an important <em>part</em> of the learning process, but you can only call yourself a painter after struggling to make your own paintings first.</p>
<p>There are a lot of similarities between learning to paint and learning to program. The only way to truly learn programming is through practice!</p>
<p>So, let's get started. Follow along with the code playbacks below. Click the links below to load each code playback (it may help to open them in a new tab). Click on the playback comments on the left-hand side of the playback screen to step through the code's development.</p>
<h3 id="heading-flow-of-control"><strong>Flow of Control</strong></h3>
<p>The following playback explains the <strong>flow of control</strong> in a program by describing how to print to the screen from it:</p>
<ul>
<li><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/1/1">1.1 Name printer program</a></li>
</ul>
<h3 id="heading-variables-and-types"><strong>Variables and Types</strong></h3>
<p>This next group of programs describes declaring variables to hold data in a program. All variables have a <strong>type</strong> which specifies what can be stored in them and what operations can be performed on them.</p>
<ul>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/1/2">1.2 Distance formula</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/1/3">1.3 Basic types in C++</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/1/4">1.4 Number types</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/1/5">1.5 Characters and strings</a></p>
</li>
</ul>
<h3 id="heading-reading-from-the-keyboard"><strong>Reading from the Keyboard</strong></h3>
<p>This final group of programs builds on previous concepts and shows how to prompt the user for input.</p>
<ul>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/1/6">1.6 Weekly pay calculator</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/1/7">1.7 Distance formula revisited</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/1/8">1.8 Gas Mileage</a></p>
</li>
</ul>
<h3 id="heading-hands-on-practice"><strong>Hands-On Practice</strong></h3>
<p>Now that you have reviewed the guided code walk-throughs, write a program that prompts the user for three integers: one representing an hour, one representing a minute, and one representing a second. Next, calculate the number of seconds until midnight based on the time that was input. Print the number of seconds until midnight on the screen.</p>
<p>Then, prompt the user for a single integer representing the number of seconds until midnight. From that value, do the reverse calculation to find the hour, minute, and second of that time. Print it to the screen in a time format HH:MM:SS.</p>
<h2 id="heading-part-2-selection"><strong>Part 2: Selection</strong></h2>
<p>This section discusses altering the flow of control with <code>if/else</code> statements. These statements ask the computer to evaluate whether a condition is <code>true</code> or <code>false</code> and changes the flow of control based on the answer. It also explains the data type, <code>bool</code>, which can hold either true or false and it shows a few examples of how to use selection with <code>if</code>, <code>if/else</code>, <code>if/else if/else</code>, and <code>switch</code> statements.</p>
<ul>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/2/1">2.1 Booleans</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/2/2">2.2 Even/odd calculator</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/2/3">2.3 Overtime pay with an if/else</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/2/4">2.4 Water temperature</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/2/5">2.5 Switch</a></p>
</li>
</ul>
<h3 id="heading-hands-on-practice-1"><strong>Hands-On Practice</strong></h3>
<p>Now that you have reviewed the guided code walk-throughs, try to write a few programs:</p>
<h4 id="heading-problem-1"><strong>Problem 1</strong></h4>
<p>Problem 1 asks you to write a program to determine if one date comes after another. The program will ask for two sets of dates. Next, the program will determine if the first date comes before, is equal to, or comes after.</p>
<pre><code class="lang-plaintext">Enter in the first month: 2
Enter in the first day: 21
Enter in the first year: 2012

Enter in the second month: 2
Enter in the second day: 22
Enter in the second year: 2011

The first date comes after the second.
</code></pre>
<h4 id="heading-problem-2"><strong>Problem 2</strong></h4>
<p>Problem 2 asks you to write a program that prompts the user for a date and determines if that date is valid. For example, 9/19/2017 is a valid date, but these are not valid dates:</p>
<ul>
<li><p>4/31/2006 (only 30 days in April)</p>
</li>
<li><p>2/29/2005 (not a leap year)</p>
</li>
<li><p>16/1/2010 (invalid month)</p>
</li>
<li><p>4/59/2013 (invalid day)</p>
</li>
</ul>
<p>If the date is correct, print it out. If it is incorrect, display an error message explaining why the date is not correct.</p>
<h4 id="heading-problem-3"><strong>Problem 3</strong></h4>
<p>Problem 3 asks you to write a program that will calculate change for a sales purchase. Your program should prompt for a sales price. Validate that the data entered is a number greater than 0. If the data entered is incorrect, display an error message and end the program.</p>
<p>Next, prompt the user for the amount that the customer will pay to the cashier. Validate that this value is greater than or equal to the sales price. If it is not, display an error message and end the program.</p>
<p>If the entry is correct, your program must calculate the amount of change to return to the user. Next, calculate what bills and coins that the the cashier needs to return to the customer. The fewest number of paper bills and coins should be returned. You can make change in many different combinations, but the only correct implementation is the one that returns the fewest paper bills and coins.</p>
<p>Display the number of each of the bills and coins. Here is a sample run of the program:</p>
<pre><code class="lang-plaintext">Enter in a sales amount: $20.38
Enter in the amount the customer pays: $30.00

The change due back is $9.62

You should give the customer this change:
0 $100 bills
0 $50 bills
0 $20 bills
0 $10 bills
1 $5 bills
4 $1 bills
1 Half Dollars
0 Quarters
1 Dimes
0 Nickels
2 Pennies
</code></pre>
<p>Because of the way arithmetic works with float variables, storing the monetary values as floats may cause some problems.</p>
<p>For example, if you had a float variable that held 1.29 to represent $1.29 and you subtracted the .05 from it (to represent giving back a nickel), you would think that you would be left with exactly 1.24. Unfortunately, the computer might store that value or it might store 1.2399999 or 1.2400001 instead of exactly 1.24.</p>
<p>These very small inconsistencies can cause a problem calculating the number of pennies to return. Consider converting the amounts into ints to solve this problem.</p>
<h2 id="heading-part-3-looping"><strong>Part 3: Looping</strong></h2>
<p>This group of playbacks discusses repeatedly executing the same code over and over again in a loop. They show how to create count controlled and event controlled loops with the <code>while</code> keyword, nested loops, a <code>for</code> loop, and how to exit a loop with <code>break</code> and <code>continue</code>.</p>
<ul>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/3/1">3.1 A simple loop</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/3/2">3.2 More loops</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/3/3">3.3 Summation</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/3/4">3.4 Nested loop</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/3/5">3.5 <code>for</code> loop</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/3/6">3.6 Capitalization</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/3/7">3.7 <code>break</code> and <code>continue</code></a></p>
</li>
</ul>
<h3 id="heading-hands-on-practice-2"><strong>Hands-On Practice</strong></h3>
<p>Now that you have reviewed the guided code walk-throughs, write a few programs:</p>
<h4 id="heading-problem-1-1"><strong>Problem 1</strong></h4>
<p>Problem 1 asks you to write a program that will calculate the sum of the squares from 1 up to and including that number. For example, if the user entered in the value 5, then the sum of the squares for the numbers one through five would be (1 + 4 + 9 + 16 + 25) = 55.</p>
<p>Your program should repeatedly calculate this value until the user enters in a value of -99. This is the sentinel value that the program uses to determine when to quit.</p>
<pre><code class="lang-plaintext">Enter in an integer number(ex. 10), -99 to quit: 5
The sum of the squares up to 5 is 55

Enter in an integer number(ex. 10), -99 to quit: 4
The sum of the squares up to 4 is 30

Enter in an integer number(ex. 10), -99 to quit: -99
Have a nice day!
</code></pre>
<h4 id="heading-problem-2-1"><strong>Problem 2</strong></h4>
<p>Problem 2 asks you to write a program that will determine whether a number is prime or not. A prime number is any number that is evenly divisible only by the number one and itself.</p>
<p>7 is prime because the only numbers that divide into it without a remainder are 1 and 7.</p>
<p>12 is not prime because the numbers that divide into it evenly are 1, 2, 3, 4, 6, and 12.</p>
<p>Your program will prompt for a number and then display whether the number is prime or not. The number entered must be a positive number. Repeatedly prompt for a number until a positive number is entered.</p>
<h4 id="heading-problem-3-1"><strong>Problem 3</strong></h4>
<p>Problem 3 asks you to calculate a mortgage schedule for someone thinking of buying a new house. The inputs to determine a monthly schedule are the principal loan amount and the annual interest rate. Assume this will be a conventional 30 year loan.</p>
<p>Your program should prompt for these inputs and find a monthly payment using this calculation:</p>
<pre><code class="lang-plaintext">                                   monthly interest rate                                              
monthly payment =  ------------------------------------------------- * principal
                   1 - (1 + monthly interest rate)^-number of months
</code></pre>
<p>Notice that you will have to calculate the monthly interest rate (the annual interest rate divided by 12.0) and number of months (360 for a 30 year loan). The ^ in this formula means raise one number to a power. There is a function called <code>pow()</code> which raises one number to another and returns the result. For example, if you wanted to raise 2 to the -3rd power, you would do this:</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">float</span> result = <span class="hljs-built_in">pow</span>(<span class="hljs-number">2.0</span>, <span class="hljs-number">-3.0</span>);
</code></pre>
<p>After you have calculated the monthly payment, create a summary of the loan characteristics. Display the loan amount, the interest rate, the monthly payment, the total amount paid for the loan, the total amount of interest paid, and the ratio of amount paid over the principal.</p>
<p>After you have printed the summary, you can begin to make the schedule. Prompt the user for the ending month to display in the schedule. The schedule should display the month number, the monthly payment, the amount paid in principal in that month, the amount paid in interest in that month, and the amount remaining in the principal (the amount paid in principle each month is deducted from the remaining principle). A month's interest amount is equal to monthly interest rate times the remaining principal. The monthly principal is the difference between the monthly payment and the monthly interest paid. Remember to update the remaining principal after every month.</p>
<p>After each year of the schedule has been printed, display a message with the year number.</p>
<h2 id="heading-part-4-arrays"><strong>Part 4: Arrays</strong></h2>
<p>This batch of programs shows how to use arrays in C/C++. An array is a collection of variables (all of the same type) that have a single name and sit next to each other in memory. Loops are almost always used to go through the elements of an array. They show how to create two and three dimensional arrays and use the random number generator in C/C++.</p>
<ul>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/4/1">4.1 Arrays</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/4/2">4.2 Average and standard deviation of an array of values</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/4/3">4.3 Problems with arrays</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/4/4">4.4 Flipping coins</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/4/5">4.5 Multi-dimensional arrays</a></p>
</li>
</ul>
<h3 id="heading-hands-on-practice-3"><strong>Hands-On Practice</strong></h3>
<p>Now that you have reviewed the guided code walk-throughs, write a few programs:</p>
<h4 id="heading-problem-1-2"><strong>Problem 1</strong></h4>
<p>Problem 1 asks you to write a program that displays a menu with three options.</p>
<p>The first option allows the user to enter in a month number (between 1-12) and a day number within that month (1-31) and calculates the day number in the year. January 1 is day 1. January 31 is day 31. February 1 is day 32. February 28 is day 59. December 31 is day 365 (don’t worry about leap years). If the user enters an invalid combination (like February 31) the program should continuously prompt the user to enter in a new value until they enter a valid date.</p>
<p>The second menu option allows the user to enter in a day number (1-365) and prints out the month name and day number of that month. If the user enters in 59, the program should print out:</p>
<pre><code class="lang-plaintext">Day 59 is February 28
</code></pre>
<p>If the user enters an invalid day number, the program should continuously prompt the user to enter in a new value until it is in the correct range.</p>
<p>The last menu option allows the user to quit the program. The menu should repeatedly be displayed until the user chooses to quit the program.</p>
<p>Use an array of integers to hold the number of days in each of the months. Use the array to keep a running sum to help with your day calculations. You may also want to create an array of strings with the month names.</p>
<pre><code class="lang-cpp"><span class="hljs-keyword">int</span> numDaysInMonths[] = {<span class="hljs-number">31</span>, <span class="hljs-number">28</span>, <span class="hljs-number">31</span>, <span class="hljs-number">30</span>, <span class="hljs-number">31</span>, <span class="hljs-number">30</span>, <span class="hljs-number">31</span>, <span class="hljs-number">31</span>, <span class="hljs-number">30</span>, <span class="hljs-number">31</span>, <span class="hljs-number">30</span>, <span class="hljs-number">31</span>};
</code></pre>
<p>Here is a sample run of the program:</p>
<pre><code class="lang-plaintext">1. Enter in a month and day
2. Enter in a day number
3. Quit
Enter in a menu option: 1

Enter in a month number: 2
Enter in a day number: 1

February 1 is day 32

1. Enter in a month and day
2. Enter in a day number
3. Quit
Enter in a menu option: 2

Enter in a day number: 59
Day 59 is February 28

1. Enter in a month and day
2. Enter in a day number
3. Quit
Enter in a menu option: 3
</code></pre>
<h4 id="heading-problem-2-2"><strong>Problem 2</strong></h4>
<p>Problem 2 asks you to create a program that will find the number of days in between two dates.</p>
<p>For example, say a user would like to know how many days are in between two dates (1/1/2000) and (3/19/2030). The program must find out how many whole days are in between these two dates (include the starting and ending date).</p>
<p>If the two dates are in the same year, then the algorithm to find the number of days may be different than if the dates are in different years. Your program must handle each case.</p>
<p>Here is a sample run of the program:</p>
<pre><code class="lang-plaintext">Enter in a start month: 1
Enter in a start day: 1
Enter in a start year: 2000

Enter in an end month: 3
Enter in an end day: 19
Enter in an end year: 2030

There are 11035 days in between 1/1/2000 and 3/19/2030
</code></pre>
<h2 id="heading-part-5-functions"><strong>Part 5: Functions</strong></h2>
<p>This group of playbacks describes another flow of control-altering mechanism called functions. A function is a named block of code that can be <em>called</em> and the flow of control will jump to it.</p>
<p>When calling a function, some data can be passed into it (called parameters) and the function can return a piece of data when it is complete (called a return value). These playbacks discuss passing in data 'by value' versus 'by reference'. They show that every variable has a limited lifetime that it sits in memory, or scope.</p>
<ul>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/5/1">5.1 Functions</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/5/2">5.2 Value returning functions</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/5/3">5.3 Functions with parameters</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/5/4">5.4 Passing parameters by reference</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/5/5">5.5 The scope of variables</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/5/6">5.6 Prime number function</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/5/7">5.7 Passing arrays to functions</a></p>
</li>
</ul>
<h3 id="heading-hands-on-practice-4"><strong>Hands-On Practice</strong></h3>
<p>Now that you have reviewed the guided code walk-throughs, write a few programs:</p>
<h4 id="heading-problem-1-3"><strong>Problem 1</strong></h4>
<p>Problem 1 asks you to extend the prime number problem from a previous section. Write a program that includes a function that will print all the prime numbers in a range. The program will ask for a lower bound and an upper bound and print all the primes in that range.</p>
<h4 id="heading-problem-2-3"><strong>Problem 2</strong></h4>
<p>Problem 2 asks you to write a function that takes an integer year number and returns a <code>bool</code> whether that year is a leap year or not. The calculation for leap year is as follows:</p>
<ul>
<li><p>most years evenly divisible by four are leap years</p>
</li>
<li><p>if a year is divisible by four and is a century year, like 1800 or 1900, then it is NOT a leap year</p>
</li>
<li><p>if a century year also happens to be divisible by 400, like 2000 or 2400, then it is a leap year</p>
</li>
</ul>
<p>The function should look something like this:</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">isLeapYear</span><span class="hljs-params">(<span class="hljs-keyword">int</span> year)</span>
</span>{
    <span class="hljs-comment">//calculate if it is leap year or not and return true or false</span>
}
</code></pre>
<h4 id="heading-problem-3-2"><strong>Problem 3</strong></h4>
<p>Problem 3 asks you to write a program that will print a calendar for a whole year given the day that January 1st falls on. Your program will prompt for the day that January 1st falls on and the year. You must print the calendar for all 12 months of that year. The options for the first day of the year will be entered with the first three letters of each of the seven days of the week from Sunday ('sun') to Saturday ('sat').</p>
<p>The year does not need to be validated, but the day of the week does. You should not allow the user to enter in a value other than 'sun', 'mon', 'tue', 'wed', 'thu', 'fri', or 'sat'.</p>
<p>The format of your calendar should be very similar to the example below. You must use at least one function in addition to the main function. Pass data between functions, do not use global variables.</p>
<p>The final requirement is that your program should only display one month at a time and then wait for some user input before continuing.</p>
<p>It is very important that you come up with a plan to solve this program before you begin coding (as it is with every program). Think about how you would print a single month's calendar given the day of the month that it begins on.</p>
<p>Sample Output:</p>
<pre><code class="lang-plaintext">What year do you want the calendar for?
2003

What day of the week does January 1st fall on (sun for Sunday, mon for Monday, etc..)?
s
Invalid Entry - please enter the first three letters of the day

What day of the week does January 1st fall on (sun for Sunday, mon for Monday, etc..)?
wed

    January 2003
 S  M  T  W  T  F  S
---------------------
          1  2  3  4
 5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Do you want to continue? y

   February 2003
 S  M  T  W  T  F  S
---------------------
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28

Do you want to continue? y
...
...
</code></pre>
<h2 id="heading-part-6-vectors"><strong>Part 6: Vectors</strong></h2>
<p>C++ comes with the Standard Template Library (STL) which is a collection of different containers. This section will cover vectors. A <code>vector</code> is an array-based container that holds data very much like an array does but it is <em>smarter</em>. It knows how many elements are in it and it can grow as the program is running. It also shows how to read and write data from a file, efficiently search through an array-based container, sort values, and more.</p>
<ul>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/6/1">6.1 <code>vector</code></a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/6/2">6.2 Passing a <code>vector</code> to a function</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/6/3">6.3 Advanced features of <code>vector</code>s</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/6/4">6.4 Reading from a file and storing in a <code>vector</code></a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/6/5">6.5 Linear search and binary search</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/6/6">6.6 Bubble sort</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/6/7">6.7 Writing to a file</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/6/8">6.8 Two dimensional <code>vector</code>s</a></p>
</li>
</ul>
<h3 id="heading-hands-on-practice-5"><strong>Hands-On Practice</strong></h3>
<p>Now that you have reviewed the guided code walk-throughs, write a program to read in whole words from a file and store them in a vector of strings. Create a simple text file (.txt) using your coding editor. Then add a few sentences of text to it. Strip any punctuation marks from the words and make them all lowercase letters. Only store a word in the vector if it is not already present.</p>
<h2 id="heading-part-7-structured-data"><strong>Part 7: Structured Data</strong></h2>
<p>This section describes how to use structured data types. Structured data types allow you to group related data together so that it can be passed around easily.</p>
<ul>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/7/1">7.1 Simple <code>struct</code></a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/7/2">7.2 Hierarchical <code>struct</code>s</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/7/3">7.3 <code>vector</code>s as members of <code>struct</code>s</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/7/4">7.4 <code>struct</code>s with a <code>vector</code> of objects</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/7/5">7.5 Calculus with <code>struct</code>s</a></p>
</li>
</ul>
<h3 id="heading-hands-on-practice-6"><strong>Hands-On Practice</strong></h3>
<p>Now that you have reviewed the guided code walk-throughs, write a program that has a struct to represent a cell phone. Every cell phone has a model name and manufacturer along with a camera with a mega-pixel resolution. For example, a user may want to store information about an Apple IPhone X with a 12 MP camera or a Google Pixel 4 with a 16 MP camera.</p>
<p>Your program will create three cell phone objects, fill them, and then add them to a vector. Lastly, print out the information about each cell phone on the screen.</p>
<p>Write a function that takes a cell phone object by reference and prompts the user to enter in the model, manufacturer, and camera resolution. Write another function that prints information about a cell phone. Write a function that takes a vector of cell phones and prints each one.</p>
<h2 id="heading-part-8-pointers"><strong>Part 8: Pointers</strong></h2>
<p>This section describes pointers in C/C++. A pointer is a variable that holds the address of another variable. Pointers are important because they allow us to use a special section of memory called the 'heap'. This section discusses the different types of memory that can be used in a program (global, local, and dynamic).</p>
<ul>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/8/1">8.1 Simple pointers</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/8/2">8.2 Pointer to an object</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/8/3">8.3 Vectors of pointers</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/8/4">8.4 Arrays are pointers</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/8/5">8.5 Passing data to functions with pointers</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/8/6">8.6 Comparing pointers</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/8/7">8.7 Three types of variables- global, local, and dynamic</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/8/8">8.8 Dynamic variables example</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/8/9">8.9 Dangling pointers and null pointers</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/8/10">8.10 Dynamic array of students</a></p>
</li>
</ul>
<h3 id="heading-hands-on-practice-7"><strong>Hands-On Practice</strong></h3>
<p>Now that you have reviewed the guided code walk-throughs, write a program that will read a sequence of words from the keyboard and store them in a dynamic array of strings. Use the word 'quit' as the word that terminates the input. Print the words back to the screen in the order in which they were entered, each on its own line. Do not store the same word twice.</p>
<p>Up until now, the size of an array has been determined at compile time. Now that you know about pointers and about the keyword <code>new</code>, write a program which is not restricted to selecting an upper bound at compile time for the number of words which can be read in.</p>
<p>One way to do this is to use <code>new</code> to create arrays of strings on the fly. Each time an array fills up, dynamically create an array which is twice as large, copy over the contents of the existing array to the new array, and continue (remember to <code>delete</code> the original array). Start with an array of 5 elements.</p>
<p>Here is an example of the output:</p>
<pre><code class="lang-plaintext">Enter in some text and end with the word quit:
This lab asks you to write a program that will read a sequence of words from the keyboard and store them in a dynamic array of strings. Use the word 'quit' as the word that terminates the input. Print the words back to the screen in the order in which they were entered each on its own line. Do not store the same word twice. quit

Doubling Array from 5 to 10
Doubling Array from 10 to 20
Doubling Array from 20 to 40
Doubling Array from 40 to 80
1. This
2. lab
3. asks
4. you
5. to
6. write
7. a
8. program
9. that
10. will
11. read
12. sequence
13. of
14. words
15. from
16. the
17. keyboard
18. and
19. store
20. them
21. in
22. dynamic
23. array
24. strings.
25. Use
26. word
27. 'quit'
28. as
29. terminates
30. input.
31. Print
32. back
33. screen
34. order
35. which
36. they
37. were
38. entered
39. each
40. on
41. its
42. own
43. line.
44. Do
45. not
46. same
47. twice.
Press any key to continue . . .
</code></pre>
<h2 id="heading-part-9-object-oriented-programming"><strong>Part 9: Object-Oriented Programming</strong></h2>
<p>This section discusses object-oriented programming using classes in C++. A class is like a <code>struct</code>, except that in addition to collecting data, it also collects methods that work on that data. This is called encapsulation. It also discusses inheritance and polymorphism that make it easier to reuse code.</p>
<ul>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/9/1">9.1 Simple class</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/9/2">9.2 A class with data members</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/9/3">9.3 A class with objects for data members</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/9/4">9.4 Common word analysis</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/9/5">9.5 Student and course registration system</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/9/6">9.6 Inheritance and polymorphism</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/9/7">9.7 Shape inheritance hierarchy</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/9/8">9.8 Inheritance and polymorphism in C++</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/9/9">9.9 Copy Constructor Example</a></p>
</li>
</ul>
<h3 id="heading-hands-on-practice-8"><strong>Hands-On Practice</strong></h3>
<p>Now that you have reviewed the guided code walk-throughs, write a few programs:</p>
<h4 id="heading-problem-1-4"><strong>Problem 1</strong></h4>
<p>Problem 1 asks you to create a Date class to represent a date.</p>
<p>There should be an int for the day number, month number, and year number declared in the private section. Instead of having a setter for each of those, have one method called setDate(int m, int d, int y) that sets the date. You can have a getter method for each piece of data.</p>
<p>Add two constructors: one that takes no data and sets the date to 1/1/2000, and another that takes three ints to set the date.</p>
<p>Include a print() method that will print the date in this format: MM/DD/YYYY and a method called printLong() that prints in this format: <code>MonthName Day, Year</code>. For example, the first day of the 21st century should print <code>January 1, 2000</code>.</p>
<p>Include a method to add some number of days, months, and years to a Date:</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">addDays</span><span class="hljs-params">(<span class="hljs-keyword">int</span> d)</span>
<span class="hljs-keyword">void</span> <span class="hljs-title">addMonths</span><span class="hljs-params">(<span class="hljs-keyword">int</span> m)</span>
<span class="hljs-keyword">void</span> <span class="hljs-title">addYears</span><span class="hljs-params">(<span class="hljs-keyword">int</span> y)</span></span>
</code></pre>
<h4 id="heading-problem-2-4"><strong>Problem 2</strong></h4>
<p>Problem 2 asks you to create some related classes for playing card games. The data needed to be stored for a card is a numeric value and a suit. A card is responsible for being able to display itself. For example, when we want to display a two of hearts, ten of diamonds, jack of clubs, or ace of spades, the output would look like this:</p>
<pre><code class="lang-plaintext">2 of Hearts
10 of Diamonds
J of Clubs
A of Spades
</code></pre>
<p>Next, create a deck class. A deck is a collection of fifty-two cards. Each card is unique. There should be a card with a numeric value from two to the ace in each of the four suits. The responsibilities of the deck class are that it must be able to shuffle itself and deal out some number of cards from the deck. To shuffle the deck, you will need to randomly move cards around in the deck. You can generate a random number in C++ using the rand() function.</p>
<p>When dealing out cards, the user will ask the deck for some number of cards. If there are enough cards in the deck, it should deal those cards out. Once a card is dealt from the deck, it cannot be dealt again from the same deck. The user will pass in a vector of cards and the deck will fill it with the number of cards requested. If there aren't enough cards in the deck, print an error message and then kill the program with an exit(0).</p>
<p>When creating a class, you always have to think about the data needed for the class and the responsibilities of the class. The data for the class should be private and an interface to the class should be provided.</p>
<p>Think about how each card and deck should be constructed. Write at least one constructor for each class. Write the card class first and test it in a driver program. Then work on the deck class.</p>
<p>To test the deck, create a deck object, ask for 52 cards, and print each of those cards to the screen.</p>
<p>Next, alter the program so that it will allow a person to evaluate the probabilities of getting certain poker hands. Poker is a card game played with a deck of 52 cards. In this program, you only need to handle the five card variety of poker. The program will repeatedly get groups of five cards from the deck and count how many times each hand occurs.</p>
<p>In most variations of poker, the precedence of hands goes like this:</p>
<pre><code class="lang-plaintext">Royal Flush - 5 cards that are a straight (5 cards in numeric order) and a flush (5 cards that are the same suit) from the 10 to the Ace.
Straight Flush - 5 cards that are a straight (5 cards in numeric order) and a flush (5 cards that are the same suit).
Four of a Kind - any 4 cards with the same number.
Full House - three of a kind (3 cards with the same number) and a pair (two cards with the same number).
Flush - any 5 cards of the same suit.
Straight - any 5 cards in numeric order.
Three of a Kind - any 3 cards with the same number.
Two Pair - 2 sets of pairs.
Pair - any 2 cards with the same number.
High Card in your Hand - if you don't have any of the above, your high card is the best hand.
</code></pre>
<p>You will need to create additional classes with more responsibilities than the Deck and Card classes.</p>
<p>Your program needs an 'evaluator' that can look at a collection of cards and determine the best hand that can be made from those cards. In order to determine probabilities, deal a great number of hands and keep track of how many times each hand shows up.</p>
<p>In other words, your program might create 100,000 collections of five cards to be evaluated. The evaluator will count how many times a royal flush comes up, how many times a straight flush comes up, an so on. Your program will show the probabilities as percentages of the likelihood of getting each hand.</p>
<p>Below is a driver to illustrate how to use the evaluator:</p>
<pre><code class="lang-cpp"><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span>
</span>{
    <span class="hljs-comment">//five card evaluator</span>
    <span class="hljs-comment">//create a poker evaluator for 5 card poker</span>
    PokerEvaluator fiveCardPokerEvaluator;

    <span class="hljs-comment">//set the number of hands to play - one hundred thousand this time</span>
    fiveCardPokerEvaluator.setNumberOfHandsToPlay(<span class="hljs-number">100000</span>);

    <span class="hljs-comment">//play all the hands and track the statistics, then print the results to the screen</span>
    fiveCardPokerEvaluator.playAndDisplay();

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<h2 id="heading-part-10-data-structures"><strong>Part 10: Data Structures</strong></h2>
<p>This section describes how to build some common data structures: a hash table, a binary search tree, and a graph. It also describes how to use the STL <code>unordered_map</code> class.</p>
<ul>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/10/1">10.1 Simple linked list</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/10/2">10.2 Simple hash table</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/10/3">10.3 More complex hash table</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/10/4">10.4 STL <code>unordered_map</code></a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/10/5">10.5 Binary search tree</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/10/6">10.6 Graph adjacency matrix</a></p>
</li>
</ul>
<h3 id="heading-hands-on-practice-9"><strong>Hands-On Practice</strong></h3>
<p>Now that you have reviewed the guided code walk-throughs, write a program that includes a class which is equivalent of the <code>vector</code> called SafeArray. SafeArray has a method called <code>at</code> that returns the element at the specified position.</p>
<p>A SafeArray maintains a pointer to an array on the heap. Use the pointer to make the array grow and shrink with calls to <code>push_back</code> and <code>pop_back</code>.</p>
<p>The SafeArray will have a method called <code>size</code> that returns the number of items in it. Include a default constructor that sets the initial size of the underlying array to hold 10 elements. Include a destructor to <code>delete</code> the array when the SafeArray falls out of scope.</p>
<h2 id="heading-part-11-sqlite-databases"><strong>Part 11: SQLite Databases</strong></h2>
<p>This section describes working with a SQLite database. SQLite is my favorite Database Management System (DBMS), because it is powerful and easy to add to any program. This section assumes that you are familiar with relational database design and SQL.</p>
<p>The first program shows how to use the API to write and run SQL queries in a C++ program. In the second, some of the repetitive code is abstracted into a separate class. In the third, transactions in SQLite are explained and it shows that they can be used to ensure the ACID properties of a database:</p>
<ul>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/11/1">11.1 The C++ SQLite API</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/11/2">11.2 An Object Oriented Auction Program</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cppbook/chapter/11/3">11.3 SQLite Transactions</a></p>
</li>
</ul>
<h3 id="heading-hands-on-practice-10"><strong>Hands-On Practice</strong></h3>
<p>Extend the auction program from the second playback to include a method that prints the names and email addresses of all of the users who won an auction. Then write a method that prints an item followed by the names and email addresses of anyone who made a bid on the item.</p>
<h2 id="heading-comments-and-feedback"><strong>Comments and Feedback</strong></h2>
<p>You can find all of these code playbacks in my free book, <a target="_blank" href="https://playbackpress.com/books/cppbook/">An Animated Introduction to Programming in C++</a>. There are more free books here:</p>
<ul>
<li><p><a target="_blank" href="https://playbackpress.com/books/pybook">An Animated Introduction to Programming with Python</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/sqlbook">Database Design and SQL for Beginners</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/workedsqlbook">Worked SQL Examples</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/sqlitebook">Programming with SQLite</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/webdevbook">An Introduction to Web Development from Back to Front</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/cljbook">An Animated Introduction to Clojure</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/exbook">An Animated Introduction to Elixir</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/rubybook">A Brief Introduction to Ruby</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/flutterbook">Mobile App Development with Dart and Flutter</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/patternsbook">OO Design Patterns with Java</a></p>
</li>
<li><p><a target="_blank" href="https://playbackpress.com/books/wordzearchbook">How I Built It: Word Zearch</a></p>
</li>
</ul>
<p>Comments and feedback are welcome via email: <a target="_blank" href="mailto:mark@playbackpress.com">mark@playbackpress.com</a>.</p>
<p>If you'd like to support my work and help keep Playback Press free for all, consider donating using <a target="_blank" href="https://github.com/sponsors/markm208">GitHub Sponsors</a>. I use all of the donations for hosting costs. Your support helps me continue creating educational content like this. Thank you!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Start Learning TypeScript – A Beginner's Guide ]]>
                </title>
                <description>
                    <![CDATA[ JavaScript is the most widely-used programming language for web development. But it lacks type-checking support, which is an essential feature of modern programming languages. JavaScript was originally designed as a simple scripting language. Its loo... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/start-learning-typescript-beginners-guide/</link>
                <guid isPermaLink="false">6792ea9437616d62ca07fe03</guid>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Programming Blogs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Technical writing  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Akande Olalekan Toheeb ]]>
                </dc:creator>
                <pubDate>Fri, 24 Jan 2025 01:19:16 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1737681395105/19aeca8f-e763-4833-9ac3-5c4db7d12fe7.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>JavaScript is the most widely-used programming language for web development. But it lacks type-checking support, which is an essential feature of modern programming languages.</p>
<p>JavaScript was originally designed as a simple scripting language. Its loose nature and absence of crucial <strong>Object-Oriented Programming (OOP)</strong> features pose certain challenges for developers:</p>
<ol>
<li><p>Limited documentation and auto-completion.</p>
</li>
<li><p>Inability to utilise OOP concepts.</p>
</li>
<li><p>Lack of type safety, leading to runtime errors.</p>
</li>
<li><p>Challenges in refactoring and maintenance.</p>
</li>
<li><p>Absence of interfaces and integration points.</p>
</li>
</ol>
<p>TypeScript solves these problems. It was built to make JavaScript a more perfect modern programming language. It helps improve the developer experience, offers many useful features, and improves interoperability.</p>
<p>This article dives into TypeScript basics. I’ll teach you how to install TS and set up a project. Then we’ll cover some important fundamentals. You’ll also learn how TypeScript compiles into JavaScript, making it compatible with browsers and Node.js environments.</p>
<h3 id="heading-what-well-coverheading-what-well-cover"><a class="post-section-overview" href="#heading-what-well-cover">What we’ll cover:</a></h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-getting-started-how-to-install-typescript">Getting Started – How to Install TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-organize-your-typescript-projects">How to Organize Your TypeScript Projects</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-typing-works-in-typescript">How Typing Works in TypeScript</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-typing-techniques">Typing Techniques</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-static-vs-dynamic-typing-in-typescript">Static vs. Dynamic Typing in TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-type-inference-and-union-types">Type Inference and Union Types</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-handle-objects-arrays-and-function-types-in-typescript">How to Handle Objects, Arrays, and Function Types in TypeScript</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-object-types-in-typescript">Object Types in TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-array-types-in-typescript">Array Types in TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-arrays-in-typescript">How to Use Arrays in TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-function-types-in-typescript">Function Types in TypeScript</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-custom-types-in-typescript">How to Create Custom Types in TypeScript</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-the-type-keyword">The Type Keyword</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-typescript-interfaces">TypeScript Interfaces</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-to-use-interfaces">When to Use Interfaces</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-generics-and-literal-types">Generics and Literal Types</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-merge-types-in-typescript">How to Merge Types in TypeScript</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-when-to-use-each-approach">When to Use Each Approach</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-bundling-and-transformations-in-typescript">Bundling and Transformations in TypeScript</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-building-better-code-with-typescript">Building Better Code with TypeScript</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before diving into TypeScript, it's important to have a foundational understanding of certain concepts to ensure a smoother learning journey. While TypeScript enhances JavaScript with static typing and other powerful features, it builds on core JavaScript principles. Here's what you should know:</p>
<h4 id="heading-1-javascript-fundamentals"><strong>1. JavaScript Fundamentals</strong></h4>
<p>TypeScript is a superset of JavaScript, meaning it extends JavaScript's capabilities. To effectively learn TypeScript, you should first have a solid grasp of JavaScript basics, including:</p>
<ul>
<li><p><strong>Syntax and data types:</strong> Understand how to declare variables (<code>let</code>, <code>const</code>, and <code>var</code>), work with primitive types (strings, numbers, booleans), and manage arrays and objects.</p>
</li>
<li><p><strong>Control flow:</strong> Be familiar with loops (<code>for</code>, <code>while</code>), conditionals (<code>if-else</code>, <code>switch</code>), and how they control program execution.</p>
</li>
<li><p><strong>Functions:</strong> Know how to define and invoke functions, work with parameters, return values, and understand concepts like arrow functions and closures.</p>
</li>
<li><p><strong>Object-Oriented Programming (OOP):</strong> Learn about creating and working with objects, classes, and inheritance. TypeScript's class-based features build heavily on JavaScript's OOP model.</p>
</li>
<li><p><strong>Error handling:</strong> Understand how to use <code>try-catch</code> blocks to handle runtime errors.</p>
</li>
</ul>
<h4 id="heading-2-basic-html-and-css"><strong>2. Basic HTML and CSS</strong></h4>
<p>Although TypeScript is a language used primarily with JavaScript, having a basic understanding of HTML and CSS is helpful, especially for front-end developers. This is because most TypeScript projects involve creating or working with web applications</p>
<ul>
<li><p><strong>HTML:</strong> Understand how to structure web pages using tags, attributes, and elements.</p>
</li>
<li><p><strong>CSS:</strong> Learn how to style elements using selectors, properties, and values. Familiarity with CSS frameworks like Bootstrap is a bonus.</p>
</li>
</ul>
<h4 id="heading-3-familiarity-with-development-tools"><strong>3. Familiarity with Development Tools</strong></h4>
<ul>
<li><p><strong>A code editor</strong> like Visual Studio Code, which has excellent TypeScript support and extensions.</p>
</li>
<li><p><strong>Node.js and npm:</strong> Understand how to set up a development environment, run JavaScript outside the browser, and use npm (Node Package Manager) to install dependencies.</p>
</li>
<li><p><strong>Version control (Git):</strong> Learn the basics of Git to track changes and collaborate effectively on TypeScript projects.</p>
</li>
</ul>
<h2 id="heading-getting-started-how-to-install-typescript">Getting Started – How to Install TypeScript</h2>
<p>To get started working with TypeScript you’ll need to install it. It’s not a complicated process. With TypeScript installed, you can leverage its power to create high-quality solutions.</p>
<p>You can install TS in two ways:</p>
<ol>
<li><strong>Global Installation</strong>: enables you to access the compiler from any directory on your machine. To install TypeScript globally, execute the following command:</li>
</ol>
<pre><code class="lang-bash">npm install -g typescript
</code></pre>
<p>This command leverages the Node.js package manager, <code>npm</code>. It installs TypeScript globally, making the command available in the command line.</p>
<ol start="2">
<li><strong>Local Installation</strong>: in this case, TypeScript is installed only within a specific project. This method ensures version compatibility and consistency across team members. To install TypeScript locally, execute the following command:</li>
</ol>
<pre><code class="lang-bash">npm install typescript --save-dev
</code></pre>
<p>Different from global installation, this command installs TypeScript as a development dependency. The <code>tsc</code> command is only available for project-specific usage, that is the specific project where you run the command.</p>
<p><strong>Can you seamlessly install TypeScript now? I hope so!</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737293526617/2f630f4c-c74f-4525-a291-9febf06d8d8b.gif" alt="2f630f4c-c74f-4525-a291-9febf06d8d8b" class="image--center mx-auto" width="480" height="340" loading="lazy"></p>
<h2 id="heading-how-to-organize-your-typescript-projects">How to Organize Your TypeScript Projects</h2>
<p>Organizing a TypeScript project involves structuring its files with meaningful names and directories, separating concerns, and using modules for encapsulation and reusability.</p>
<p>The <code>.ts</code> extension denotes typeScript files and contains code that converts into JavaScript for execution.</p>
<p>TypeScript also supports <code>.d.ts</code> files, also known as type definition files. These files offer type information about external JavaScript libraries or modules, aiding in better type-checking and code completion as well as improving development efficiency. Below is an example of a good TS project structure:</p>
<pre><code class="lang-plaintext">my-ts-project/
├── src/ 
│   ├── components/ 
│   │   ├── Button.tsx
│   │   ├── Input.tsx
│   │   └── Modal.tsx
│   ├── services/ 
│   │   ├── api.ts
│   │   └── authService.ts
│   ├── utils/ 
│   │   ├── helpers.ts 
│   │   └── validators.ts
│   ├── models/ 
│   │   ├── User.ts
│   │   └── Product.ts
│   ├── index.tsx 
│   └── styles/ 
│       ├── global.css
│       └── theme.css
├── public/ 
│   ├── index.html
│   └── assets/ 
│       ├── images/
│       └── fonts/
├── tsconfig.json
└── package.json
</code></pre>
<p>Let’s understand what’s going on here:</p>
<ol>
<li><p><code>src/</code>: This directory houses all the source code for the project.</p>
<ul>
<li><p><code>components/</code>: Contains reusable UI components (for example, <code>Button</code>, <code>Input</code>, <code>Modal</code>). Using <code>.tsx</code> (TypeScript JSX) allows you to write JSX with type safety.</p>
</li>
<li><p><code>services/</code>: Holds services that interact with external APIs or handle application logic (for example, <code>api.ts</code> for API calls, <code>authService.ts</code> for authentication).</p>
</li>
<li><p><code>utils/</code>: Contains helper functions and utility classes for common tasks (for example, <code>helpers.ts</code> for date formatting, <code>validators.ts</code> for input validation).</p>
</li>
<li><p><code>models/</code>: Defines TypeScript interfaces or classes to represent data structures (for example, <code>User.ts</code>, <code>Product.ts</code>).</p>
</li>
<li><p><code>index.tsx</code>: The main entry point of the application.</p>
</li>
<li><p><code>styles/</code>: Contains CSS or other styling files.</p>
</li>
</ul>
</li>
<li><p><code>public/</code>: This directory contains static assets that are not processed by TypeScript (for example, HTML, images, fonts).</p>
</li>
<li><p><code>tsconfig.json</code>: The TypeScript configuration file, specifying compiler options.</p>
</li>
<li><p><code>package.json</code>: The project's manifest file, listing dependencies, scripts, and other project metadata.</p>
</li>
</ol>
<p>Just a quick note about naming conventions so you understand them here:</p>
<ul>
<li><p>Use PascalCase for class names (for example, <code>User</code>, <code>Product</code>).</p>
</li>
<li><p>Use camelCase for function names and variable names (for example, <code>getUser</code>, <code>firstName</code>).</p>
</li>
<li><p>Use meaningful and descriptive names for files and directories.</p>
</li>
</ul>
<p>This structure promotes modularity, reusability, and better organization, making your TypeScript projects easier to maintain and scale.</p>
<p>Properly organizing your TS projects enhances code maintainability, readability, and collaboration in TypeScript development workflows.</p>
<h2 id="heading-how-typing-works-in-typescript">How Typing Works in TypeScript</h2>
<p>Like any other typed programming language, TypeScript relies on type definitions, generally called <strong>Typing</strong>.</p>
<p>Typing is a term used in programming to define data types for variables, method parameters, and return values within the code.</p>
<p>Typing allows you to catch errors quickly and early in development, a superpower that helps maintain better code quality.</p>
<p>To specify a type in TypeScript, put a colon( <code>:</code>) and the desired data type after your variable name. Here’s an example:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> age: <span class="hljs-built_in">number</span> = <span class="hljs-number">2</span>;
</code></pre>
<p>The above variable is declared with the <code>number</code> type. In TypeScript, this means it can store numbers only and nothing else.</p>
<h3 id="heading-typing-techniques">Typing Techniques</h3>
<p>In TypeScript, data can be typed in two main ways:</p>
<ol>
<li><strong>Static Typing</strong>: Static typing refers to explicitly specifying the data type of variables and other entities in the code during development. The TypeScript compiler enforces these type definitions, helping to catch type-related errors early. For example:</li>
</ol>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> age: <span class="hljs-built_in">number</span> = <span class="hljs-number">25</span>;
</code></pre>
<p>Here, the variable <code>age</code> is explicitly declared to have the <code>number</code> type. This ensures that only numeric values can be assigned to it, reducing the risk of runtime errors.</p>
<ol start="2">
<li><strong>Dynamic Typing</strong>: Dynamic typing in TypeScript refers to scenarios where the type of a variable is determined at runtime. This can occur when variables are assigned the <code>any</code> type, which allows them to hold values of any type. TypeScript does not perform type-checking on operations involving variables with the <code>any</code> type.</li>
</ol>
<pre><code class="lang-typescript"><span class="hljs-keyword">let</span> value: <span class="hljs-built_in">any</span>;
value = <span class="hljs-number">25</span>; <span class="hljs-comment">// Number</span>
value = <span class="hljs-string">"Hello"</span>; <span class="hljs-comment">// String</span>
</code></pre>
<p>While TypeScript is primarily a statically typed language, dynamic typing can still be useful in specific cases, such as:</p>
<ul>
<li><p>Working with third-party libraries that lack type definitions.</p>
</li>
<li><p>Interfacing with dynamically structured data (for example, JSON responses from APIs with unknown structures).</p>
</li>
<li><p>Rapid prototyping or when type information is unavailable during the initial development phase.</p>
</li>
</ul>
<h3 id="heading-static-vs-dynamic-typing-in-typescript">Static vs. Dynamic Typing in TypeScript</h3>
<p>Static typing is significantly more common in TypeScript, as it is one of the core features that sets TypeScript apart from JavaScript. By enforcing strict type checks, static typing enhances code maintainability, reduces bugs, and improves developer productivity.</p>
<p>Dynamic typing is typically reserved for cases where flexibility is required or when dealing with data whose structure cannot be determined in advance. Just keep in mind that relying heavily on dynamic typing (for example, overusing the <code>any</code> type) is generally discouraged, as it undermines the benefits of TypeScript's static typing system.</p>
<p>So while dynamic typing has its place in certain edge cases, static typing is the preferred and more commonly used approach in TypeScript development.</p>
<h3 id="heading-type-inference-and-union-types">Type Inference and Union Types</h3>
<h4 id="heading-type-inference"><strong>Type Inference</strong></h4>
<p>Type inference is a powerful TypeScript feature that allows the compiler to automatically deduce the type of a variable based on the value assigned to it during initialization. In simpler terms, TypeScript looks at the value you assign to a variable and decides what type it should be, even if you don’t explicitly declare the type.</p>
<p>For example:</p>
<pre><code class="lang-typescript">typescriptCopyEditlet age = <span class="hljs-number">25</span>; <span class="hljs-comment">// TypeScript infers that 'age' is of type 'number'</span>
age = <span class="hljs-string">"hello"</span>; <span class="hljs-comment">// Error: Type 'string' is not assignable to type 'number'</span>
</code></pre>
<p>In this example, the <code>age</code> variable is automatically inferred as a <code>number</code> because of its initial value, <code>25</code>. Any attempt to reassign <code>age</code> to a value of a different type (like a string) will result in a type error.</p>
<p>Type inference is particularly useful because it reduces the need for explicit type annotations, making your code cleaner and more readable. However, it still provides the safety and reliability of TypeScript's type-checking.</p>
<h5 id="heading-when-to-use-type-inference">When to use type inference:</h5>
<ul>
<li><p><strong>Simple assignments</strong>: Use type inference for straightforward assignments where the type is obvious from the value.</p>
</li>
<li><p><strong>Default values</strong>: When providing default values for variables or function parameters, type inference ensures the correct type is applied without requiring manual annotations.</p>
</li>
<li><p><strong>Rapid prototyping</strong>: During early stages of development, type inference can reduce boilerplate code while still enforcing type safety.</p>
</li>
</ul>
<h4 id="heading-union-types"><strong>Union Types</strong></h4>
<p>Union types allow a variable to hold values of multiple types. They are defined by placing a pipe (<code>|</code>) between the types. This feature is particularly useful when a variable may legitimately have more than one type during its lifecycle.</p>
<p>For example:</p>
<pre><code class="lang-typescript">typescriptCopyEditlet numOrString: <span class="hljs-built_in">number</span> | <span class="hljs-built_in">string</span>; <span class="hljs-comment">// 'numOrString' can hold either a number or a string</span>
numOrString = <span class="hljs-number">25</span>; <span class="hljs-comment">// Valid</span>
numOrString = <span class="hljs-string">"hello"</span>; <span class="hljs-comment">// Valid</span>
numOrString = <span class="hljs-literal">true</span>; <span class="hljs-comment">// Error: Type 'boolean' is not assignable to type 'number | string'</span>
</code></pre>
<p>You can even define union types with more than two possible types:</p>
<pre><code class="lang-typescript">typescriptCopyEditlet multiType: <span class="hljs-built_in">number</span> | <span class="hljs-built_in">string</span> | <span class="hljs-built_in">boolean</span>;
multiType = <span class="hljs-number">42</span>; <span class="hljs-comment">// Valid</span>
multiType = <span class="hljs-string">"TypeScript"</span>; <span class="hljs-comment">// Valid</span>
multiType = <span class="hljs-literal">false</span>; <span class="hljs-comment">// Valid</span>
</code></pre>
<h5 id="heading-when-to-use-union-types">When to use union types:</h5>
<ul>
<li><p><strong>Flexible function parameters</strong>: When a function can accept multiple types of input.</p>
<pre><code class="lang-typescript">  typescriptCopyEditfunction printValue(value: <span class="hljs-built_in">string</span> | <span class="hljs-built_in">number</span>) {
    <span class="hljs-built_in">console</span>.log(value);
  }
</code></pre>
</li>
<li><p><strong>Handling diverse data structures</strong>: When working with APIs or external data sources where fields may vary in type.</p>
</li>
<li><p><strong>Optional or multi-state variables</strong>: For example, a variable that can represent a loading state as a boolean, an error as a string, or valid data as an object:</p>
<pre><code class="lang-typescript">  typescriptCopyEditlet status: <span class="hljs-built_in">boolean</span> | <span class="hljs-built_in">string</span> | { success: <span class="hljs-built_in">boolean</span>; data: <span class="hljs-built_in">any</span> };
</code></pre>
</li>
</ul>
<h2 id="heading-how-to-handle-objects-arrays-and-function-types-in-typescript">How to Handle Objects, Arrays, and Function Types in TypeScript</h2>
<p>To master TypeScript, you must understand the various data types supported in TypeScript and how and when to use them.</p>
<p>The <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#primitive_values">JavaScript primitive types</a> such as <em>strings</em>, <em>numbers</em>, <em>booleans</em>, and more also define the fundamental building blocks of data in TypeScript. But in particular, <code>Objects</code><em>,</em> <code>Arrays</code><em>,</em> and <code>Functions</code> are essential for building robust applications. With objects, arrays, and functions, you can better handle data and use them efficiently in development.</p>
<h3 id="heading-object-types-in-typescript">Object Types in TypeScript</h3>
<p>Object types represent the blueprint for creating objects in TypeScript. You can use objects to define their shape, similar to how <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes">classes</a> are used in <strong>object-oriented programming (OOP)</strong>. But objects lack the behavioral aspects and encapsulation that classes offer.</p>
<p>To define an object type, explicitly define the object's blueprint after the colon(<code>:</code>). For Example:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Object Type Initialization</span>

<span class="hljs-keyword">let</span> student: {
    name: <span class="hljs-built_in">string</span>;
    age: <span class="hljs-built_in">number</span>;
    matricNumber: <span class="hljs-built_in">string</span> | <span class="hljs-built_in">number</span>;
 };

<span class="hljs-comment">// Assigning the Object with actual data</span>

student = {
    name: <span class="hljs-string">"Akande"</span>
    age: <span class="hljs-number">21</span>,
    matricNumber: <span class="hljs-number">21</span>/<span class="hljs-number">52</span> + <span class="hljs-string">"HP"</span> + <span class="hljs-number">19</span>,
};
</code></pre>
<p>Notice that the properties end with a semi-colon<code>;</code> instead of a comma <code>,</code> which ends them in an actual object.</p>
<p>The above is the primary way to define an object in TypeScript. Another way is to use <code>interfaces</code>, which I’ll cover later in this article.</p>
<h3 id="heading-array-types-in-typescript">Array Types in TypeScript</h3>
<p>Arrays in TypeScript allow you to store multiple values of the same or different data types in a single variable. They enhance the safety and clarity of your code by enforcing type consistency across the array elements.</p>
<p>In TypeScript, array types can be defined in two ways:</p>
<h4 id="heading-1-using-the-array-model"><strong>1. Using the</strong> <code>Array&lt;type&gt;</code> model</h4>
<p>This syntax uses the generic <code>Array</code> type, where <code>type</code> represents the type of elements the array can hold.</p>
<pre><code class="lang-typescript">typescriptCopyEditlet numbers: <span class="hljs-built_in">Array</span>&lt;<span class="hljs-built_in">number</span>&gt; = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];
<span class="hljs-keyword">let</span> mixedArray: <span class="hljs-built_in">Array</span>&lt;<span class="hljs-built_in">number</span> | <span class="hljs-built_in">string</span>&gt; = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-string">"Hello"</span>];
</code></pre>
<ul>
<li><p><code>numbers</code> Example: This array can only contain numbers. Attempting to add a string or other type to this array will result in a type error.</p>
<pre><code class="lang-typescript">  typescriptCopyEditnumbers.push(<span class="hljs-number">6</span>); <span class="hljs-comment">// Valid</span>
  numbers.push(<span class="hljs-string">"Hello"</span>); <span class="hljs-comment">// Error: Type 'string' is not assignable to type 'number'</span>
</code></pre>
</li>
<li><p><code>mixedArray</code> Example: This array uses a union type (<code>number | string</code>), allowing it to store both numbers and strings.</p>
<pre><code class="lang-typescript">  typescriptCopyEditmixedArray.push(<span class="hljs-number">42</span>); <span class="hljs-comment">// Valid</span>
  mixedArray.push(<span class="hljs-string">"TypeScript"</span>); <span class="hljs-comment">// Valid</span>
  mixedArray.push(<span class="hljs-literal">true</span>); <span class="hljs-comment">// Error: Type 'boolean' is not assignable to type 'number | string'</span>
</code></pre>
</li>
</ul>
<h4 id="heading-2-using-the-type-model"><strong>2. Using the</strong> <code>type[]</code> model</h4>
<p>This syntax appends square brackets (<code>[]</code>) to the type of elements the array can hold.</p>
<pre><code class="lang-typescript">typescriptCopyEditconst numbers: <span class="hljs-built_in">number</span>[] = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];
<span class="hljs-keyword">const</span> mixedArray: (<span class="hljs-built_in">string</span> | <span class="hljs-built_in">number</span>)[] = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-string">"Hello"</span>];
</code></pre>
<ul>
<li><p><code>numbers</code> Example: Similar to the <code>Array&lt;number&gt;</code> example, this array can only hold numbers.</p>
<pre><code class="lang-typescript">  typescriptCopyEditnumbers[<span class="hljs-number">0</span>] = <span class="hljs-number">10</span>; <span class="hljs-comment">// Valid</span>
  numbers.push(<span class="hljs-string">"Hi"</span>); <span class="hljs-comment">// Error: Type 'string' is not assignable to type 'number'</span>
</code></pre>
</li>
<li><p><code>mixedArray</code> Example: Like the earlier <code>mixedArray</code>, this array allows both numbers and strings, providing flexibility where the type of data may vary.</p>
<pre><code class="lang-typescript">  typescriptCopyEditmixedArray[<span class="hljs-number">1</span>] = <span class="hljs-string">"World"</span>; <span class="hljs-comment">// Valid</span>
  mixedArray.push(<span class="hljs-literal">true</span>); <span class="hljs-comment">// Error: Type 'boolean' is not assignable to type 'string | number'</span>
</code></pre>
</li>
</ul>
<h3 id="heading-how-to-use-arrays-in-typescript"><strong>How to Use Arrays in TypeScript</strong></h3>
<p>Arrays are versatile and commonly used for storing collections of related data. Here are a few practical scenarios:</p>
<p><strong>Storing Homogeneous Data:</strong><br>When all elements in the array share the same type, such as a list of user IDs or product prices:</p>
<pre><code class="lang-typescript">typescriptCopyEditconst userIds: <span class="hljs-built_in">number</span>[] = [<span class="hljs-number">101</span>, <span class="hljs-number">102</span>, <span class="hljs-number">103</span>];
<span class="hljs-keyword">const</span> productPrices: <span class="hljs-built_in">Array</span>&lt;<span class="hljs-built_in">number</span>&gt; = [<span class="hljs-number">29.99</span>, <span class="hljs-number">49.99</span>, <span class="hljs-number">19.99</span>];
</code></pre>
<p><strong>Storing Heterogeneous Data:</strong><br>When elements can have different types, such as a list of messages containing text and optional metadata:</p>
<pre><code class="lang-typescript">typescriptCopyEditconst messages: (<span class="hljs-built_in">string</span> | <span class="hljs-built_in">object</span>)[] = [
  <span class="hljs-string">"Welcome"</span>,
  { <span class="hljs-keyword">type</span>: <span class="hljs-string">"error"</span>, text: <span class="hljs-string">"Something went wrong"</span> },
];
</code></pre>
<p><strong>Iterating Over Arrays:</strong><br>Arrays in TypeScript can be used in loops with full type safety:</p>
<pre><code class="lang-typescript">typescriptCopyEditconst scores: <span class="hljs-built_in">number</span>[] = [<span class="hljs-number">80</span>, <span class="hljs-number">90</span>, <span class="hljs-number">70</span>];
scores.forEach(<span class="hljs-function">(<span class="hljs-params">score</span>) =&gt;</span> <span class="hljs-built_in">console</span>.log(score + <span class="hljs-number">5</span>)); <span class="hljs-comment">// Adds 5 to each score</span>
</code></pre>
<p><strong>Function Parameters and Return Types:</strong><br>Arrays can also be passed as function parameters or returned by functions with strict typing:</p>
<pre><code class="lang-typescript">typescriptCopyEditfunction getNumbers(): <span class="hljs-built_in">number</span>[] {
  <span class="hljs-keyword">return</span> [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">printStrings</span>(<span class="hljs-params">strings: <span class="hljs-built_in">string</span>[]</span>): <span class="hljs-title">void</span> </span>{
  strings.forEach(<span class="hljs-function">(<span class="hljs-params">str</span>) =&gt;</span> <span class="hljs-built_in">console</span>.log(str));
}
</code></pre>
<h3 id="heading-function-types-in-typescript">Function Types in TypeScript</h3>
<p>Function types in TypeScript describe the shape of functions, including parameter types and return types. Function types are defined by explicitly specifying the parameter types during declaration. The return type is specified by adding <code>:</code> and the type to return immediately after the brackets. For Example:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addition</span> (<span class="hljs-params">a: <span class="hljs-built_in">number</span>, b: <span class="hljs-built_in">number</span></span>): <span class="hljs-title">number</span> </span>{
<span class="hljs-keyword">return</span> a + b;
}
</code></pre>
<p>The above function takes in two numbers, adds them, and returns a number. The function will not work if any of its arguments are not numbers and if it returns anything else except a number. For example:</p>
<ol>
<li>Calling the function with a string as the argument:</li>
</ol>
<pre><code class="lang-typescript"><span class="hljs-comment">// This won't work because it expects numbers, and one of the arguments is a string</span>

addition(<span class="hljs-number">1</span>, <span class="hljs-string">"two"</span>);
</code></pre>
<ol start="2">
<li>Re-writing the function to return a string:</li>
</ol>
<pre><code class="lang-typescript"><span class="hljs-comment">// Function will return an error because it's returning a string</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addition</span> (<span class="hljs-params">a: <span class="hljs-built_in">number</span>, b: <span class="hljs-built_in">number</span></span>): <span class="hljs-title">string</span> </span>{
    <span class="hljs-keyword">let</span> result = a + b;
    <span class="hljs-keyword">let</span> returnStatement = <span class="hljs-string">`Addition of <span class="hljs-subst">${a}</span> and <span class="hljs-subst">${b}</span> is: <span class="hljs-subst">${result}</span>`</span>;
    <span class="hljs-keyword">return</span> returnStatement;
}
</code></pre>
<p>Test the code out for yourself to see how these examples work.</p>
<p>Understanding and effectively handling objects, arrays, and functions in TypeScript empowers you to write type-safe and maintainable code, enhancing the reliability and scalability of your applications.</p>
<h2 id="heading-how-to-create-custom-types-in-typescript">How to Create Custom Types in TypeScript</h2>
<p>Often, your design pattern doesn't follow the built-in data types in TypeScript. For example, you might have patterns that use dynamic programming). And this can cause problems in your codebase. TypeScript offers a solution for creating <strong>custom types</strong> to address this issue.</p>
<p>Custom types allow you to define your data structure and shapes according to your needs. This enhances code readability and maintainability.</p>
<h3 id="heading-the-type-keyword">The Type Keyword</h3>
<p>The <code>type</code> keyword lets you create <strong>type aliases</strong>, providing a way to create custom types. The types you create can be reused throughout your codebase. Type aliases help define union types or combine types into single aliases. The syntax for creating a custom type is as follows:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Syntax</span>

<span class="hljs-keyword">type</span> TypeAlias = <span class="hljs-keyword">type</span>;
</code></pre>
<p>And here’s an example:</p>
<p><img src="https://i.ibb.co/qBZ3Zcw/Screenshot-2024-02-16-at-4-17-27-PM.png" alt="type Example" width="600" height="400" loading="lazy"></p>
<p>The code above creates a custom-type <code>UserName</code>, a union of numbers and strings. It uses the type created to define two variables relatively to check if the type works.</p>
<p>Note that it’s recommended to start a type alias starts with a capital letter.</p>
<p>The type Keyword is generally used for primitives – but how about creating a custom object type?</p>
<p>This is where <strong>Interfaces</strong> come in.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1737294121435/5be475e2-efae-428e-b9ed-bbcce7ce260d.jpeg" alt="5be475e2-efae-428e-b9ed-bbcce7ce260d" class="image--center mx-auto" width="1200" height="627" loading="lazy"></p>
<h3 id="heading-typescript-interfaces">TypeScript Interfaces</h3>
<p>Interfaces in TypeScript are used to define the structure of objects. They serve as blueprints, specifying the properties an object should have and their respective types. This ensures that objects conform to a consistent shape, enabling type safety and clearer code.</p>
<h4 id="heading-defining-an-interface">Defining an interface</h4>
<p>An interface is defined using the <code>interface</code> keyword. The syntax looks like this:</p>
<pre><code class="lang-typescript">typescriptCopyEditinterface InterfaceName {
  property1: Type;
  property2: Type;
}
</code></pre>
<h4 id="heading-example">Example:</h4>
<pre><code class="lang-typescript">typescriptCopyEditinterface User {
  id: <span class="hljs-built_in">number</span>;
  name: <span class="hljs-built_in">string</span>;
  email: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">const</span> user: User = {
  id: <span class="hljs-number">1</span>,
  name: <span class="hljs-string">"Alice"</span>,
  email: <span class="hljs-string">"alice@example.com"</span>,
};
</code></pre>
<p>Here’s what’s going on in this example:</p>
<ol>
<li><p><strong>Interface declaration (</strong><code>interface User</code>):</p>
<ul>
<li><p>Here, we define a blueprint for a <code>User</code> object. It specifies that any object of type <code>User</code> must have the following properties:</p>
<ul>
<li><p><code>id</code> of type <code>number</code></p>
</li>
<li><p><code>name</code> of type <code>string</code></p>
</li>
<li><p><code>email</code> of type <code>string</code></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Using the interface (</strong><code>const user: User</code>):</p>
<ul>
<li><p>We declare an object <code>user</code> of type <code>User</code>.</p>
</li>
<li><p>The object is required to have all the properties defined in the <code>User</code> interface, with values of the specified types. If a property is missing or its type doesn't match, TypeScript will throw a compile-time error.</p>
</li>
</ul>
</li>
</ol>
<p>    For example:</p>
<pre><code class="lang-typescript">    typescriptCopyEditconst invalidUser: User = {
      id: <span class="hljs-number">1</span>,
      name: <span class="hljs-string">"Alice"</span>,
      <span class="hljs-comment">// Error: Property 'email' is missing in type</span>
    };
</code></pre>
<p>So you might be wondering – why should you use interfaces?</p>
<ul>
<li><p><strong>Type safety</strong>: Ensures that objects conform to the expected structure, preventing runtime errors.</p>
</li>
<li><p><strong>Reusability</strong>: The same interface can be reused across different parts of the application, reducing duplication.</p>
</li>
<li><p><strong>Code clarity</strong>: Makes the code easier to read and understand by explicitly describing the shape of objects.</p>
</li>
</ul>
<h4 id="heading-advanced-features-of-interfaces">Advanced Features of Interfaces</h4>
<ol>
<li><p><strong>Optional properties</strong>: You can make properties optional by adding a question mark (<code>?</code>).</p>
<pre><code class="lang-typescript"> typescriptCopyEditinterface Product {
   id: <span class="hljs-built_in">number</span>;
   name: <span class="hljs-built_in">string</span>;
   description?: <span class="hljs-built_in">string</span>; <span class="hljs-comment">// Optional property</span>
 }

 <span class="hljs-keyword">const</span> product: Product = {
   id: <span class="hljs-number">101</span>,
   name: <span class="hljs-string">"Laptop"</span>,
 }; <span class="hljs-comment">// Valid, as 'description' is optional</span>
</code></pre>
</li>
<li><p><strong>Readonly properties</strong>: Use <code>readonly</code> to prevent properties from being modified after initialization.</p>
<pre><code class="lang-typescript"> typescriptCopyEditinterface Point {
   <span class="hljs-keyword">readonly</span> x: <span class="hljs-built_in">number</span>;
   <span class="hljs-keyword">readonly</span> y: <span class="hljs-built_in">number</span>;
 }

 <span class="hljs-keyword">const</span> point: Point = { x: <span class="hljs-number">10</span>, y: <span class="hljs-number">20</span> };
 point.x = <span class="hljs-number">15</span>; <span class="hljs-comment">// Error: Cannot assign to 'x' because it is a read-only property</span>
</code></pre>
</li>
<li><p><strong>Extending interfaces</strong>: Interfaces can inherit properties from other interfaces, enabling composition.</p>
<pre><code class="lang-typescript"> typescriptCopyEditinterface Person {
   name: <span class="hljs-built_in">string</span>;
   age: <span class="hljs-built_in">number</span>;
 }

 <span class="hljs-keyword">interface</span> Employee <span class="hljs-keyword">extends</span> Person {
   employeeId: <span class="hljs-built_in">number</span>;
 }

 <span class="hljs-keyword">const</span> employee: Employee = {
   name: <span class="hljs-string">"John"</span>,
   age: <span class="hljs-number">30</span>,
   employeeId: <span class="hljs-number">1234</span>,
 };
</code></pre>
</li>
</ol>
<h3 id="heading-when-to-use-interfaces"><strong>When to Use Interfaces</strong></h3>
<p>There are various scenarios when it’s a good idea to use interfaces. You can use them when you want to define and enforce the structure of objects passed around in your code.</p>
<p>They’re also useful in API responses, as they help you type-check objects received from APIs. This ensures that the data conforms to your expectations.</p>
<p>Interfaces are also handy when working with reusable types. When multiple parts of your application use objects with the same structure, interfaces prevent duplication.</p>
<p>By leveraging interfaces, you can create robust, maintainable, and type-safe applications. They are an essential feature of TypeScript that promotes clean and predictable code.</p>
<h3 id="heading-generics-and-literal-types">Generics and Literal Types</h3>
<p><strong>Generics</strong> in TypeScript allow you to create reusable components that can work with various data types. They let you write functions, classes, and interfaces without specifying the exact type upfront, making your code more flexible and maintainable.</p>
<p>Here's an example of a generic function and a generic interface in TypeScript:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Generic interface for a box that can hold any value </span>

<span class="hljs-keyword">interface</span>  Box&lt;T&gt; { 
    value: T; 
}

<span class="hljs-comment">// Usage examples</span>

<span class="hljs-keyword">let</span>  numberBox: Box&lt;<span class="hljs-built_in">number</span>&gt; = { value: <span class="hljs-number">10</span> };
<span class="hljs-keyword">let</span>  stringBox: Box&lt;<span class="hljs-built_in">string</span>&gt; = { value: <span class="hljs-string">"TypeScript"</span> };

<span class="hljs-built_in">console</span>.log(numberBox.value); <span class="hljs-comment">// Output: 10  </span>
<span class="hljs-built_in">console</span>.log(stringBox.value); <span class="hljs-comment">// Output: TypeScript</span>
</code></pre>
<p>You can use generics when you’re unsure of your data type.</p>
<p>In contrast to Generics, <strong>Literal types</strong> allow you to specify exact values a variable can hold. This adds increased specificity and type safety to your code, preventing unintended values from being assigned. Here’s an example:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> Direction = <span class="hljs-string">'up'</span> | <span class="hljs-string">'down'</span> | <span class="hljs-string">'left'</span> | <span class="hljs-string">'right'</span>;
</code></pre>
<p>A variable created with the above type can only be assigned for the strings up, down, left, and right.</p>
<p>Overall, leveraging custom types in TypeScript empowers you to create expressive, reusable, and type-safe data structures, helping you develop more robust and maintainable applications.</p>
<h2 id="heading-how-to-merge-types-in-typescript">How to Merge Types in TypeScript</h2>
<p>Merging types in TypeScript combines multiple type declarations into a single, unified type. This capability allows developers to build complex types from smaller, reusable pieces, enhancing code clarity, reusability, and maintainability.</p>
<h3 id="heading-1-declaration-merging-in-interfaces"><strong>1. Declaration Merging in Interfaces</strong></h3>
<p>TypeScript supports <strong>declaration merging</strong>, where multiple interface declarations with the same name are automatically combined into a single interface. This lets you augment an existing interface by defining additional properties or methods.</p>
<h5 id="heading-example-1"><strong>Example:</strong></h5>
<pre><code class="lang-typescript">typescriptCopyEditinterface User {
  id: <span class="hljs-built_in">number</span>;
  name: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">interface</span> User {
  email: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">const</span> user: User = {
  id: <span class="hljs-number">1</span>,
  name: <span class="hljs-string">"Alice"</span>,
  email: <span class="hljs-string">"alice@example.com"</span>,
};
</code></pre>
<h5 id="heading-how-it-works"><strong>How it works:</strong></h5>
<ul>
<li><p>The <code>User</code> interface is declared twice, each with different properties.</p>
</li>
<li><p>TypeScript automatically merges these declarations into a single interface:</p>
<pre><code class="lang-typescript">  typescriptCopyEditinterface User {
    id: <span class="hljs-built_in">number</span>;
    name: <span class="hljs-built_in">string</span>;
    email: <span class="hljs-built_in">string</span>;
  }
</code></pre>
</li>
<li><p>When creating the <code>user</code> object, all properties from the merged interface must be present. If any property is missing, TypeScript will raise an error.</p>
</li>
</ul>
<p>Declaration merging is particularly useful when working with third-party libraries. You can extend or add new properties to an existing interface without modifying the library's source code.</p>
<h3 id="heading-2-interface-merging-using-the-extends-keyword"><strong>2. Interface Merging Using the</strong> <code>extends</code> Keyword</h3>
<p>The <code>extends</code> keyword allows one interface to inherit properties and methods from another, creating a new interface that combines the properties of both.</p>
<h5 id="heading-example-2"><strong>Example:</strong></h5>
<pre><code class="lang-typescript">typescriptCopyEditinterface Person {
  name: <span class="hljs-built_in">string</span>;
  age: <span class="hljs-built_in">number</span>;
}

<span class="hljs-keyword">interface</span> Employee <span class="hljs-keyword">extends</span> Person {
  employeeId: <span class="hljs-built_in">number</span>;
}

<span class="hljs-keyword">const</span> employee: Employee = {
  name: <span class="hljs-string">"John"</span>,
  age: <span class="hljs-number">30</span>,
  employeeId: <span class="hljs-number">101</span>,
};
</code></pre>
<h5 id="heading-how-it-works-1"><strong>How it works:</strong></h5>
<ul>
<li><p>The <code>Person</code> interface defines two properties: <code>name</code> and <code>age</code>.</p>
</li>
<li><p>The <code>Employee</code> interface uses the <code>extends</code> keyword to inherit the properties from <code>Person</code>.</p>
</li>
<li><p>The <code>Employee</code> interface also adds a new property, <code>employeeId</code>.</p>
</li>
<li><p>The <code>employee</code> object must include all properties from both <code>Person</code> and <code>Employee</code>.</p>
</li>
</ul>
<h5 id="heading-this-approach-is-ideal-for-hierarchical-relationships-for-instance-you-can-define-a-base-interface-for-shared-properties-and-extend-it-for-specialized-types">This approach is ideal for hierarchical relationships. For instance, you can define a base interface for shared properties and extend it for specialized types.</h5>
<h3 id="heading-3-type-merging-using-the-amp-operator"><strong>3. Type Merging Using the</strong> <code>&amp;</code> Operator</h3>
<p>The <code>&amp;</code> operator, known as the intersection type, allows you to combine multiple types into a single type. The resulting type includes all properties and methods from each type.</p>
<h5 id="heading-example-3"><strong>Example:</strong></h5>
<pre><code class="lang-typescript">typescriptCopyEdittype Address = {
  city: <span class="hljs-built_in">string</span>;
  country: <span class="hljs-built_in">string</span>;
};

<span class="hljs-keyword">type</span> ContactInfo = {
  email: <span class="hljs-built_in">string</span>;
  phone: <span class="hljs-built_in">string</span>;
};

<span class="hljs-keyword">type</span> EmployeeDetails = Address &amp; ContactInfo;

<span class="hljs-keyword">const</span> employee: EmployeeDetails = {
  city: <span class="hljs-string">"New York"</span>,
  country: <span class="hljs-string">"USA"</span>,
  email: <span class="hljs-string">"john.doe@example.com"</span>,
  phone: <span class="hljs-string">"123-456-7890"</span>,
};
</code></pre>
<h5 id="heading-how-it-works-2"><strong>How it works:</strong></h5>
<ul>
<li><p><code>Address</code> and <code>ContactInfo</code> are two separate types.</p>
</li>
<li><p><code>EmployeeDetails</code> is an intersection type created using <code>Address &amp; ContactInfo</code>.</p>
</li>
<li><p>The <code>employee</code> object must include all properties from both <code>Address</code> and <code>ContactInfo</code>. Missing or incorrectly typed properties will result in a TypeScript error.</p>
</li>
</ul>
<h5 id="heading-intersection-types-are-helpful-when-you-need-to-combine-unrelated-types-or-create-composite-types-for-specific-use-cases-like-api-responses-that-merge-different-data-structures">Intersection types are helpful when you need to combine unrelated types or create composite types for specific use cases, like API responses that merge different data structures.</h5>
<h3 id="heading-when-to-use-each-of-these-approaches"><strong>When to Use Each of These Approaches</strong></h3>
<ol>
<li><p><strong>Declaration merging:</strong> Use when you want to extend or augment an existing interface, particularly in third-party libraries or shared codebases.</p>
</li>
<li><p><code>extends</code> <strong>keyword</strong>: Use for hierarchical relationships where a base interface can be specialized into more specific types.</p>
</li>
<li><p><strong>Intersection types (</strong><code>&amp;</code>): Use when you need to combine multiple unrelated types into a single type for specific use cases.</p>
</li>
</ol>
<p>By understanding these merging techniques and their implications, you can structure your TypeScript code effectively, improving reusability and maintainability while maintaining type safety.</p>
<h2 id="heading-bundling-and-transformations-in-typescript">Bundling and Transformations in TypeScript</h2>
<p>Not every browser supports the latest JavaScript used by TypeScript. So you can use the <strong>TypeScript compiler</strong>, or <code>tsc</code>, to convert TypeScript code (.ts files) into conventional JavaScript (.js files) that’s universally compatible with all browsers. <code>tsc</code> translates TypeScript-specific elements like types and classes into JavaScript code that browsers can interpret.</p>
<p>To execute TypeScript files, <code>tsc</code> is your go-to. You can install <code>tsc</code> using npm and then transform your .ts files into .js files. To use <code>tsc</code>, just specify the name of the TypeScript file before the <code>tsc</code> command. For instance, if you have a file named <code>app.ts</code>, you can run it by typing:</p>
<pre><code class="lang-bash">tsc app.ts
</code></pre>
<p>Webpack or Parcel are frequently employed to deploy TypeScript code on browsers. These tools bundle all JavaScript files, including those from TypeScript, for improved performance and easier website implementation. They also optimize code loading by reducing its size and enhancing browser speed.</p>
<h2 id="heading-building-better-code-with-typescript">Building Better Code with TypeScript</h2>
<p>Embracing TypeScript as a JavaScript developer opens up possibilities for writing more robust and maintainable code. By understanding the basics and core concepts outlined in this guide, you can leverage TypeScript's static typing system to catch errors early in development, leading to fewer bugs and smoother code maintenance.</p>
<p>By using TypeScript, JavaScript devs can enhance their code quality and productivity. As you continue to explore and practice with TypeScript, you will discover even more powerful features and functionalities.</p>
<p>Keep pushing your boundaries and dive deeper into the world of TypeScript. 😉</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
