<?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[ Ashutosh Krishna - 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[ Ashutosh Krishna - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 30 Jun 2026 10:21:01 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/ashutoshkrris/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Database Version Control with Liquibase and Spring Boot ]]>
                </title>
                <description>
                    <![CDATA[ Picture this familiar scenario: you're working on a new feature that requires a new database column. You open your local database client, write an ALTER TABLE statement, and execute it. Your code work ]]>
                </description>
                <link>https://www.freecodecamp.org/news/database-version-control-with-liquibase-and-spring-boot/</link>
                <guid isPermaLink="false">6a277a781aef44b8099e978b</guid>
                
                    <category>
                        <![CDATA[ Databases ]]>
                    </category>
                
                    <category>
                        <![CDATA[ version control ]]>
                    </category>
                
                    <category>
                        <![CDATA[ spring-boot ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Java ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Tue, 09 Jun 2026 02:29:12 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/dbadec79-8248-4ef4-aa9c-126250db7a64.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Picture this familiar scenario: you're working on a new feature that requires a new database column. You open your local database client, write an <code>ALTER TABLE</code> statement, and execute it. Your code works perfectly. You commit the Java code, push it to the repository, and go grab a coffee.</p>
<p>A few hours later, a teammate pulls your branch, runs the application, and everything crashes.</p>
<p>"Hey," they ask across the room (or in a Slack channel), "did you change the database?"</p>
<p>You quickly realize you forgot to share the SQL script. You paste it into the chat. They run it. Everything works. Then, a week later, the deployment to the staging environment fails for the exact same reason. By the time this code reaches production, everyone is asking a variation of the same terrified question: "Which SQL script should I run?"</p>
<p>This situation is called schema drift. It happens when the state of your database diverges across different environments. Staging has one schema, production has another, and every developer's local machine is a unique snowflake of untested database modifications.</p>
<p>Managing database changes manually is a recipe for deployment headaches and team collaboration challenges. Application code is stateless and easy to replace. Databases are stateful. Databases have surprisingly good memories, and they rarely forget a bad migration.</p>
<p>Liquibase solves this problem by bringing version-control discipline to your database changes. Instead of passing around SQL files and hoping people remember to run them, you define your database changes in code. These changes travel with your application repository and execute automatically.</p>
<p>Here is a high-level look at how this architecture works:</p>
<img src="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/41202410-4d42-4343-98df-912420dbfb15.png" alt="Architecture diagram showing code flowing from a developer to a database via Git, Spring Boot, and Liquibase." style="display:block;margin:0 auto" width="482" height="771" loading="lazy">

<p>Think about the journey of a single database change. A developer commits their database migration alongside their Java code into Git. When the CI/CD pipeline (or a teammate) pulls that code, the Spring Boot application starts. But before the app fully boots up and accepts web traffic, Liquibase intercepts the process. It acts as a gatekeeper, connecting to the database and applying the required schema changes. This ensures the database exactly matches the code's expectations before a single user makes a request.</p>
<h2 id="heading-why-database-version-control-matters">Why Database Version Control Matters</h2>
<p>If you've spent any time working on team-based applications, you've probably seen a folder structure that looks exactly like this:</p>
<pre><code class="language-plaintext">project-sql-scripts/
├── create_employee_table.sql
├── create_employee_table_final.sql
├── create_employee_table_final_v2.sql
├── add_email_column.sql
├── latest.sql
└── definitely_latest_use_this_one.sql
</code></pre>
<p>The phrase "just run this SQL script manually" has launched many memorable incidents.</p>
<p>When you rely on manual database updates, you guarantee failure at scale. Onboarding a new developer becomes an archeological expedition to figure out how to build the local schema. Deployments become stressful events requiring a checklist of manual queries that must be run in a highly specific order.</p>
<p>Version-controlled database changes treat your schema as code. When your database changes live alongside your application logic, you gain several immediate benefits:</p>
<ul>
<li><p><strong>Consistency:</strong> Every environment (local, staging, production) applies the exact same changes in the exact same order.</p>
</li>
<li><p><strong>Safety:</strong> You eliminate the human error of skipping a script or running an outdated query.</p>
</li>
<li><p><strong>Visibility:</strong> You can look at a Git commit and see exactly how the Java code and the database schema changed together to support a new feature.</p>
</li>
</ul>
<p>Git solved version control for code. Liquibase helps prevent databases from becoming the rebellious sibling.</p>
<h2 id="heading-what-is-liquibase">What is Liquibase?</h2>
<p>At its core, Liquibase is a database migration tool that tracks and applies schema changes in a predictable and repeatable way.</p>
<p>Instead of writing loose SQL scripts, you write "migrations" (also called changeSets). Liquibase reads these files, compares them against a tracking table inside your actual database, and figures out exactly what needs to be executed to bring the database up to date.</p>
<p>To use Liquibase effectively, you only need to understand a few conceptual terms:</p>
<ul>
<li><p><strong>changeLog:</strong> The master file. This is essentially a list that tells Liquibase which migration files to execute and in what order.</p>
</li>
<li><p><strong>changeSet:</strong> A single, atomic change to your database. Creating a table is one changeSet. Adding a column is another.</p>
</li>
<li><p><strong>Migration History:</strong> A table Liquibase automatically creates in your database (called <code>DATABASECHANGELOG</code>) to remember which changeSets have already been executed.</p>
</li>
<li><p><strong>Checksums:</strong> A unique hash generated for every changeSet. Liquibase uses this to detect if someone secretly modified a file after it was already executed.</p>
</li>
</ul>
<p>When you integrate Liquibase with Spring Boot, the migration process happens completely automatically during the application startup phase.</p>
<img src="https://cdn.hashnode.com/uploads/covers/61c1acb4a90dea775da8262b/410e7bdb-da2f-4c48-a5b2-20a45fb20956.png" alt="Sequence diagram of Spring Boot startup where Liquibase checks the tracking table, locks the database, runs migrations, and releases the lock before allowing HTTP traffic." style="display:block;margin:0 auto" width="645" height="528" loading="lazy">

<p>During startup, Liquibase takes control before your web server is allowed to receive HTTP traffic. It reaches into the database and checks the tracking table to see which migrations have already run. If it finds new migrations in your local files, it locks the database to prevent concurrent updates, executes the changes, records the new history, and finally releases the lock. Only after this entire process completes does Spring Boot finish booting up.</p>
<p>Because Liquibase runs before Spring Boot fully initializes the web server, your application will never serve traffic with an outdated database schema. If a migration fails, the application fails to start, protecting your system from entering a broken state.</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>Now that you understand the theory, let's build something real. We're going to build the database layer for an Employee Management API.</p>
<p>For this project we'll use:</p>
<ul>
<li><p>Java 17+</p>
</li>
<li><p>Spring Boot 3.x</p>
</li>
<li><p>Maven</p>
</li>
<li><p>Liquibase</p>
</li>
<li><p>H2 Database</p>
</li>
</ul>
<p>We're using H2 because it's an in-memory database that requires zero installation. You can run this project immediately without configuring Docker containers or installing database servers. But everything you learn here applies exactly the same way to PostgreSQL, MySQL, SQL Server, or Oracle.</p>
<p>If you're generating this project via <a href="https://start.spring.io/">Spring Initializr</a>, select the following dependencies: Spring Web, Spring Data JPA, Liquibase Migration, and H2 Database.</p>
<p>In your <code>pom.xml</code>, you'll see the critical dependencies that make this work:</p>
<pre><code class="language-xml">&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/artifactId&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;com.h2database&lt;/groupId&gt;
        &lt;artifactId&gt;h2&lt;/artifactId&gt;
        &lt;scope&gt;runtime&lt;/scope&gt;
    &lt;/dependency&gt;

    &lt;dependency&gt;
        &lt;groupId&gt;org.liquibase&lt;/groupId&gt;
        &lt;artifactId&gt;liquibase-core&lt;/artifactId&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;
</code></pre>
<p>Next, configure Spring Boot to talk to H2 and find your Liquibase files. Open your <code>src/main/resources/application.properties</code> file and add the following:</p>
<pre><code class="language-plaintext"># H2 Database Configuration
spring.datasource.url=jdbc:h2:file:./data/employeedb;DB_CLOSE_DELAY=-1
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# Enable H2 Console to inspect the database in your browser
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

# Liquibase Configuration
spring.liquibase.change-log=classpath:db/changelog/db.changelog-master.xml
</code></pre>
<p>That last line is the most important. It tells Spring Boot exactly where to find the "master list" of your database changes.</p>
<p>Note: We're using a file-based H2 database instead of an in-memory database. The problem with an in-memory database is that it completely wipes itself clean every time you restart Spring Boot.</p>
<p>While Liquibase will happily rebuild the schema from scratch on every boot, a <strong>file-based</strong> database is much better for this tutorial (and for real-world local development). With a file-based database, your data, and more importantly, your Liquibase history, will actually persist between application restarts.</p>
<h2 id="heading-understanding-core-liquibase-concepts">Understanding Core Liquibase Concepts</h2>
<p>Before we write our first table, we need to understand how Liquibase organizes files. Liquibase uses a hierarchical structure.</p>
<p>Think of it like a book. The <code>changeLog</code> is the table of contents, and the <code>changeSets</code> are the actual chapters.</p>
<ol>
<li><p><strong>The Master ChangeLog:</strong> This is the entry point. It rarely contains actual database changes. Instead, its only job is to include other files in a specific order.</p>
</li>
<li><p><strong>Child ChangeLogs:</strong> These group related changes together.</p>
</li>
<li><p><strong>ChangeSets:</strong> These are the actual, atomic database commands (like creating a table or adding a column).</p>
</li>
</ol>
<p>Here's a visual breakdown of how this hierarchy works in a real Spring Boot project:</p>
<img src="https://cdn.hashnode.com/uploads/covers/61c1acb4a90dea775da8262b/2cbd3054-d0e4-4129-b0a4-015b08c7258c.png" alt="File structure diagram showing a master changelog XML file pointing to three child migration files in strict chronological order." style="display:block;margin:0 auto" width="993" height="259" loading="lazy">

<p>Liquibase organizes migrations hierarchically. You maintain a single master file that acts as a table of contents. This master file rarely holds actual SQL commands. Instead, it explicitly includes child XML files in a strict execution order. Each of those child files (like <code>01-create-employees.xml</code>) contains one or more individual database commands, which Liquibase calls changeSets.</p>
<p>A <code>changeSet</code> is uniquely identified by three things:</p>
<ul>
<li><p><strong>id:</strong> A unique string (often a number or a Jira ticket ID).</p>
</li>
<li><p><strong>author:</strong> The person who wrote the migration.</p>
</li>
<li><p><strong>file path:</strong> Where the file is located.</p>
</li>
</ul>
<p>When Liquibase runs, it looks at a <code>changeSet</code>, calculates a cryptographic hash of its contents (a checksum), and records the id, author, and checksum in the database. If it sees that exact combination of id, author, and file path in the database again on the next startup, it skips it.</p>
<h2 id="heading-create-the-initial-employee-schema-version-1">Create the Initial Employee Schema (Version 1)</h2>
<p>Let's write our first version. We need a table to store employees.</p>
<p>First, create the master file at <code>src/main/resources/db/changelog/db.changelog-master.xml</code>:</p>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;databaseChangeLog
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
        http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd"&gt;

    &lt;include file="db/changelog/changes/01-create-employees.xml"/&gt;

&lt;/databaseChangeLog&gt;
</code></pre>
<p>Next, create the actual migration file at <code>src/main/resources/db/changelog/changes/01-create-employees.xml</code>:</p>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;databaseChangeLog
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
        http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd"&gt;

    &lt;changeSet id="1" author="ashutoshkrris"&gt;
        &lt;createTable tableName="employees"&gt;
            &lt;column name="id" type="BIGINT" autoIncrement="true"&gt;
                &lt;constraints primaryKey="true" nullable="false"/&gt;
            &lt;/column&gt;
            &lt;column name="first_name" type="VARCHAR(50)"&gt;
                &lt;constraints nullable="false"/&gt;
            &lt;/column&gt;
            &lt;column name="last_name" type="VARCHAR(50)"&gt;
                &lt;constraints nullable="false"/&gt;
            &lt;/column&gt;
        &lt;/createTable&gt;
    &lt;/changeSet&gt;

&lt;/databaseChangeLog&gt;
</code></pre>
<p>Let's look at what we just did. We defined a <code>changeSet</code> with an <code>id</code> of "1" and an <code>author</code> of "ashutoshkrris". Inside, we used Liquibase's XML syntax to define a table.</p>
<p>Why use XML instead of plain SQL? Because Liquibase is database-agnostic. This exact XML will generate the correct auto-increment syntax for PostgreSQL (<code>SERIAL</code>), MySQL (<code>AUTO_INCREMENT</code>), or Oracle (<code>IDENTITY</code>). You define the structure, and Liquibase translates it to the specific database dialect.</p>
<p>Now, run your Spring Boot application. Watch your terminal output. You'll see logs similar to this:</p>
<img src="https://cdn.hashnode.com/uploads/covers/61c1acb4a90dea775da8262b/69171f93-d54b-487b-a09a-9fd49cad6b6a.png" alt="Terminal Logs for Liquibase Startup" style="display:block;margin:0 auto" width="1810" height="518" loading="lazy">

<p>Liquibase realized the database was empty. It automatically created its tracking table (<code>DATABASECHANGELOG</code>), read our <code>changeSet</code>, executed the table creation, and recorded the event.</p>
<p>If you restart the application right now, Liquibase will run again. But this time, it'll check the <code>DATABASECHANGELOG</code> table, see that <code>id="1"</code> and <code>author="ashutoshkrris"</code> has already been executed, and silently skip it. Your database is now safely version-controlled.</p>
<h2 id="heading-what-just-happened">What Just Happened?</h2>
<p>Up to this point, Liquibase might feel a bit like magic. You dropped an XML file into a folder, started Spring Boot, and your database schema transformed.</p>
<p>But understanding how Liquibase actually works under the hood is critical. If you understand the startup sequence, you'll know exactly how to debug deployments when things eventually go wrong.</p>
<p>When your Spring Boot application starts, it doesn't immediately begin accepting web requests. First, it initializes its internal components. When it creates the Liquibase component, the migration process begins.</p>
<p>Here's exactly what happens during that startup phase:</p>
<img src="https://cdn.hashnode.com/uploads/covers/61c1acb4a90dea775da8262b/4625b403-3f12-4015-9f46-2d0966c35e30.png" alt="Detailed sequence diagram showing Liquibase checking the lock table, acquiring the lock, running unexecuted migrations, and releasing the lock before Tomcat starts." style="display:block;margin:0 auto" width="832" height="766" loading="lazy">

<p>Let's trace the exact sequence. When Spring Boot initializes Liquibase, the very first thing the tool does is query the lock table to ensure no other application instance is currently migrating the database. If the coast is clear, it claims the lock. It then calculates cryptographic checksums for your local XML files, compares them against the database history, executes any missing changes, and logs them. Finally, it releases the lock so the Tomcat web server can safely start.</p>
<p>This sequence guarantees that your application will never serve a user request before the database schema is completely ready to handle it.</p>
<h2 id="heading-inspecting-the-database-liquibase-metadata-tables">Inspecting the Database: Liquibase Metadata Tables</h2>
<p>Let's look at what this history and locking actually looks like inside the database itself. Since we configured the H2 Console earlier, we can inspect the raw tables.</p>
<p>While your Spring Boot application is running, open your browser and navigate to <code>http://localhost:8080/h2-console</code>. Connect using the JDBC URL <code>jdbc:h2:file:./data/employeedb</code> with the username <code>sa</code> and a blank password.</p>
<p>Inside, you'll see your <code>employees</code> table. You'll also see two extra tables created automatically by Liquibase: <code>DATABASECHANGELOG</code> and <code>DATABASECHANGELOGLOCK</code>.</p>
<h3 id="heading-the-databasechangelog-table">The <code>DATABASECHANGELOG</code> Table</h3>
<p>This table is the brain of your migration strategy. It acts as the permanent ledger of every database change ever applied to this environment.</p>
<p>If you run <code>SELECT * FROM DATABASECHANGELOG;</code>, you'll see output that looks like this:</p>
<table>
<thead>
<tr>
<th>ID</th>
<th>AUTHOR</th>
<th>FILENAME</th>
<th>DATEEXECUTED</th>
<th>ORDEREXECUTED</th>
<th>EXECTYPE</th>
<th>MD5SUM</th>
<th>DESCRIPTION</th>
<th>COMMENTS</th>
<th>TAG</th>
<th>LIQUIBASE</th>
<th>CONTEXTS</th>
<th>LABELS</th>
<th>DEPLOYMENT_ID</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>ashutoshkrris</td>
<td>db/changelog/changes/01-create-employees.xml</td>
<td>2026-05-30 13:11:35.937919</td>
<td>1</td>
<td>EXECUTED</td>
<td>9:66e7dcffb2b1902a4e9f01670cb5f192</td>
<td>createTable tableName=employees</td>
<td></td>
<td><em>null</em></td>
<td>4.31.1</td>
<td><em>null</em></td>
<td><em>null</em></td>
<td>0126894849</td>
</tr>
</tbody></table>
<p>Let's break down the most important columns:</p>
<ul>
<li><p><strong>ID, AUTHOR, FILENAME:</strong> These three columns form a composite key. Together, they uniquely identify a single migration.</p>
</li>
<li><p><strong>DATEEXECUTED &amp; ORDEREXECUTED:</strong> Tells you exactly when a script ran and in what sequence.</p>
</li>
<li><p><strong>MD5SUM:</strong> This is the cryptographic hash of your XML file. When Liquibase starts, it hashes your local XML file and compares it to this column. If you secretly edit a file after it's been executed, this hash won't match, and Liquibase will crash the startup to protect your database.</p>
</li>
<li><p><strong>EXECTYPE:</strong> Most of the time, this simply says <code>EXECUTED</code>. But it provides a crucial audit trail: if you use Liquibase commands to intentionally skip a migration but record it as finished, you'll see <code>MARK_RAN</code>. If a migration was skipped because its preconditions failed, you'll see <code>SKIPPED</code>.</p>
</li>
<li><p><strong>TAG:</strong> Think of this as a Git tag for your database schema. Before a major, high-risk deployment, you can configure Liquibase to "tag" the current state of the database (for example, <code>v1.4.0</code>). If the deployment fails catastrophically, you can trigger a rollback command telling Liquibase to undo every change applied after the <code>v1.4.0</code> tag.</p>
</li>
<li><p><strong>CONTEXTS:</strong> This is how you manage environment-specific changes. By adding a context attribute to your changeSet (for example, <code>&lt;changeSet id="7" author="ashutoshkrris" context="dev, qa"&gt;</code>), that migration will only execute if Spring Boot passes "dev" or "qa" to Liquibase on startup. Production will safely ignore it.</p>
</li>
<li><p><strong>LABELS:</strong> While Contexts target environments, Labels target categories of work. You can label a changeSet with a Jira ticket number (<code>issue-842</code>) or a release train (<code>Q3-release</code>). This allows advanced teams to selectively execute or roll back specific subsets of features without affecting the rest of the database.</p>
</li>
</ul>
<h3 id="heading-the-databasechangeloglock-table">The <code>DATABASECHANGELOGLOCK</code> Table</h3>
<p>This table is tiny, but it plays a massive role in modern deployments.</p>
<p>If you run <code>SELECT * FROM DATABASECHANGELOGLOCK;</code>, you'll see a single row:</p>
<table>
<thead>
<tr>
<th>ID</th>
<th>LOCKED</th>
<th>LOCKGRANTED</th>
<th>LOCKEDBY</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>FALSE</td>
<td><em>null</em></td>
<td><em>null</em></td>
</tr>
</tbody></table>
<p>Imagine you're deploying your Spring Boot application to a Kubernetes cluster. You tell Kubernetes to spin up three identical instances simultaneously. All three instances connect to the exact same database.</p>
<p>If all three instances try to run the <code>CREATE TABLE</code> migration at the exact same millisecond, your database will throw concurrency errors. The lock table prevents this. The very first instance to reach the database sets <code>LOCKED</code> to <code>TRUE</code>. The other two instances check the table, see the lock, and politely wait.</p>
<p><strong>Practical Troubleshooting Tip:</strong> Sometimes, a deployment fails catastrophically mid-migration (perhaps the server lost power). When this happens, Liquibase might die before it can set <code>LOCKED</code> back to <code>FALSE</code>.</p>
<p>The next time you start the application, the logs will hang indefinitely, repeating: <code>Waiting for changelog lock....</code></p>
<p>If you're absolutely certain no other applications are currently running migrations, you can manually fix this by running a simple SQL command in your database client:</p>
<pre><code class="language-sql">UPDATE DATABASECHANGELOGLOCK SET LOCKED = FALSE;
</code></pre>
<p>This forces the lock open, allowing your application to resume.</p>
<h2 id="heading-evolving-the-employee-api">Evolving the Employee API</h2>
<p>Software is never finished. Two weeks after your successful Version 1 deployment, the business team comes back with new requirements.</p>
<p>Because you now understand how Liquibase tracks history, evolving the database is simple. You just append new files to your master list.</p>
<h3 id="heading-version-2-adding-an-email-field">Version 2: Adding an Email Field</h3>
<p>The HR team needs to contact employees. You need an email column.</p>
<p>Create a new file at <code>src/main/resources/db/changelog/changes/02-add-employee-email.xml</code>:</p>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;databaseChangeLog
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
        http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd"&gt;

    &lt;changeSet id="2" author="ashutoshkrris"&gt;
        &lt;addColumn tableName="employees"&gt;
            &lt;column name="email" type="VARCHAR(100)"&gt;
                &lt;constraints nullable="false" unique="true"/&gt;
            &lt;/column&gt;
        &lt;/addColumn&gt;
    &lt;/changeSet&gt;

&lt;/databaseChangeLog&gt;
</code></pre>
<p>Add this to your <code>db.changelog-master.xml</code> file immediately below your first include:</p>
<pre><code class="language-xml">&lt;include file="db/changelog/changes/02-add-employee-email.xml"/&gt;
</code></pre>
<p>When you restart the application, Liquibase checks the <code>DATABASECHANGELOG</code> table. It sees that <code>id="1"</code> is already there, so it skips it. It sees <code>id="2"</code> is missing, so it executes it and adds a new row to the tracking table.</p>
<h3 id="heading-version-3-adding-departments-support">Version 3: Adding Departments Support</h3>
<p>The company is growing. Employees now belong to departments. You need a <code>departments</code> table and a foreign key constraint linking the two.</p>
<p>Create <code>03-add-departments.xml</code>:</p>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;databaseChangeLog
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
        http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd"&gt;

    &lt;changeSet id="3" author="ashutoshkrris"&gt;
        &lt;createTable tableName="departments"&gt;
            &lt;column name="id" type="BIGINT" autoIncrement="true"&gt;
                &lt;constraints primaryKey="true" nullable="false"/&gt;
            &lt;/column&gt;
            &lt;column name="name" type="VARCHAR(50)"&gt;
                &lt;constraints nullable="false" unique="true"/&gt;
            &lt;/column&gt;
        &lt;/createTable&gt;
    &lt;/changeSet&gt;

    &lt;changeSet id="4" author="ashutoshkrris"&gt;
        &lt;addColumn tableName="employees"&gt;
            &lt;column name="department_id" type="BIGINT"/&gt;
        &lt;/addColumn&gt;
        &lt;addForeignKeyConstraint baseTableName="employees"
                                 baseColumnNames="department_id"
                                 constraintName="fk_employee_department"
                                 referencedTableName="departments"
                                 referencedColumnNames="id"/&gt;
    &lt;/changeSet&gt;

&lt;/databaseChangeLog&gt;
</code></pre>
<p>Notice that we used two separate changeSets in one file. This is a best practice. Each changeSet represents one logical operation. If the foreign key creation (id="4") fails, the department table creation (id="3") will still be recorded as successful, and only id="4" will roll back.</p>
<h3 id="heading-version-4-amp-5-employee-status-and-performance-indexes">Version 4 &amp; 5: Employee Status and Performance Indexes</h3>
<p>Finally, HR wants to track active versus inactive staff, and the database team noticed that searching by last name is getting slow.</p>
<p>Create <code>04-status-and-indexes.xml</code>:</p>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;databaseChangeLog
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
        http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd"&gt;

    &lt;changeSet id="3" author="ashutoshkrris"&gt;
        &lt;createTable tableName="departments"&gt;
            &lt;column name="id" type="BIGINT" autoIncrement="true"&gt;
                &lt;constraints primaryKey="true" nullable="false"/&gt;
            &lt;/column&gt;
            &lt;column name="name" type="VARCHAR(50)"&gt;
                &lt;constraints nullable="false" unique="true"/&gt;
            &lt;/column&gt;
        &lt;/createTable&gt;
    &lt;/changeSet&gt;

    &lt;changeSet id="4" author="ashutoshkrris"&gt;
        &lt;addColumn tableName="employees"&gt;
            &lt;column name="department_id" type="BIGINT"/&gt;
        &lt;/addColumn&gt;
        &lt;addForeignKeyConstraint baseTableName="employees"
                                 baseColumnNames="department_id"
                                 constraintName="fk_employee_department"
                                 referencedTableName="departments"
                                 referencedColumnNames="id"/&gt;
    &lt;/changeSet&gt;

&lt;/databaseChangeLog&gt;
</code></pre>
<p>Remember to add all new files to your <code>db.changelog-master.xml</code>. The order of your include statements is the exact order Liquibase will execute them.</p>
<h2 id="heading-the-golden-rule-never-modify-executed-changesets">The Golden Rule: Never Modify Executed ChangeSets</h2>
<p>Eventually, a developer on your team will look at your <code>01-create-employees.xml</code> file and notice a mistake. Perhaps they spot a typo in a column name, or perhaps they realize a column is missing a strict non-null constraint.</p>
<p>Their instinct, based on years of writing standard Java code, will be to open that XML file, fix the mistake, save the file, and restart the application.</p>
<p>Let's actually do this and see what happens.</p>
<p>Open your <code>src/main/resources/db/changelog/changes/01-create-employees.xml</code> file. Change the <code>first_name</code> column to <code>given_name</code>:</p>
<pre><code class="language-xml">&lt;column name="given_name" type="VARCHAR(50)"&gt;
    &lt;constraints nullable="false"/&gt;
&lt;/column&gt;
</code></pre>
<p>Save the file and restart your Spring Boot application.</p>
<p>Instead of a smooth startup, your application will instantly crash, and your terminal will vomit a massive stack trace. Look closely at the top of the error logs. You should see this exact message:</p>
<pre><code class="language-shell">Caused by: liquibase.exception.ValidationFailedException: Validation Failed:
     1 changesets check sum
          db/changelog/changes/01-create-employees.xml::1::ashutoshkrris was: 9:66e7dcffb2b1902a4e9f01670cb5f192 but is now: 9:2bd3ef21343d3b5c9448cc50bc35deef
</code></pre>
<p>Here's why this happens. Once a changeSet runs against an environment, it becomes immutable history. You can't change the past.</p>
<p>When Liquibase starts up, it calculates a cryptographic hash (an MD5 checksum) of your local XML file. It then queries the <code>DATABASECHANGELOG</code> table and compares the freshly calculated hash against the hash that was recorded when the file originally executed.</p>
<p>If you change even a single character in a file that has already been executed, the hash changes. Liquibase detects the tampering and refuses to start. It does this to protect your data. If your XML code says a column is named <code>first_name</code> but the database was originally built using <code>fist_name</code>, your Spring Data JPA repositories are going to fail anyway.</p>
<h3 id="heading-how-to-fix-it-the-right-way">How to Fix It (The Right Way)</h3>
<p>If you made this mistake locally, you might be tempted to go into your database, delete the row from the <code>DATABASECHANGELOG</code> table, and try again. Don't do this. If this code reaches staging or production, you can't manually delete rows on production servers.</p>
<p>The correct way to fix a schema mistake is to <strong>roll forward</strong>.</p>
<p>First, undo your change in <code>01-create-employees.xml</code> so the hash matches the database again. Then, write a brand new changeSet to apply the fix:</p>
<pre><code class="language-xml">&lt;changeSet id="7" author="ashutosh"&gt;
    &lt;renameColumn tableName="employees" 
                  oldColumnName="first_name" 
                  newColumnName="given_name" 
                  columnDataType="VARCHAR(50)"/&gt;
&lt;/changeSet&gt;
</code></pre>
<p>Include it in your master changelog, restart the application, and the database will safely evolve to the correct state.</p>
<h2 id="heading-working-with-seed-data">Working with Seed Data</h2>
<p>Sometimes, a schema change requires initial data to be useful.</p>
<p>For example, in Version 3, we created a <code>departments</code> table. Right now, that table is completely empty. When a new developer clones the repository and spins up the project locally, they have to manually write SQL <code>INSERT</code> statements just to test the API.</p>
<p>We can automate this by making baseline data insertion part of our migration strategy.</p>
<p>Create a new file at <code>src/main/resources/db/changelog/changes/05-seed-departments.xml</code>:</p>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;databaseChangeLog
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
        http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd"&gt;

    &lt;changeSet id="8" author="ashutoshkrris"&gt;
        &lt;insert tableName="departments"&gt;
            &lt;column name="name" value="Engineering"/&gt;
        &lt;/insert&gt;
        &lt;insert tableName="departments"&gt;
            &lt;column name="name" value="Human Resources"/&gt;
        &lt;/insert&gt;
        &lt;insert tableName="departments"&gt;
            &lt;column name="name" value="Finance"/&gt;
        &lt;/insert&gt;
    &lt;/changeSet&gt;

&lt;/databaseChangeLog&gt;
</code></pre>
<p>Add the include statement to your <code>db.changelog-master.xml</code> file. When you restart the application, Liquibase will insert these rows. Your API is now instantly usable out of the box.</p>
<h3 id="heading-the-danger-of-data-migrations">The Danger of Data Migrations</h3>
<p>While seeding data is powerful, it requires discipline. Here is a practical engineering rule of thumb:</p>
<p><strong>Do use Liquibase for:</strong></p>
<ul>
<li><p>Static lookup tables (status codes, country lists, default departments).</p>
</li>
<li><p>System configuration flags required for the application to boot.</p>
</li>
</ul>
<p><strong>Do NOT use Liquibase for:</strong></p>
<ul>
<li><p>Generating thousands of fake users for testing.</p>
</li>
<li><p>Migrating massive amounts of transactional data (for example, moving 5 million records from one table to another).</p>
</li>
</ul>
<p>Large data migrations can lock up database tables for hours. If you lock a core table during a deployment, your application will experience a massive outage. Keep your changeSets focused on schema structure and essential baseline data. Use dedicated scripts or background jobs for heavy data manipulation.</p>
<h2 id="heading-rollbacks">Rollbacks</h2>
<p>In a perfect world, code always works. In reality, you'll eventually deploy a database change that breaks a critical production query or corrupts data. When this happens, you need a way to hit the undo button.</p>
<p>Liquibase supports rollbacks, but you have to understand how it interprets them.</p>
<h3 id="heading-automatic-vs-explicit-rollbacks">Automatic vs. Explicit Rollbacks</h3>
<p>Many Liquibase commands are automatically reversible. For example, if you write a changeSet to <code>&lt;createTable&gt;</code> or <code>&lt;addColumn&gt;</code>, Liquibase implicitly knows that the opposite of adding a column is dropping a column. You don't have to tell it how to undo these actions.</p>
<p>But some operations are inherently destructive or ambiguous. If you use custom <code>&lt;sql&gt;</code> tags, or if you use <code>&lt;dropTable&gt;</code>, Liquibase has no idea how to put the data back. In these cases, you must provide explicit rollback instructions.</p>
<p>Let's simulate a scenario where we add a temporary access code column, but we want to ensure we know exactly how to remove it safely.</p>
<p>Create <code>06-temporary-access.xml</code>:</p>
<pre><code class="language-xml">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;databaseChangeLog
        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
        http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.20.xsd"&gt;

    &lt;changeSet id="9" author="ashutosh"&gt;
        &lt;addColumn tableName="employees"&gt;
            &lt;column name="temp_access_code" type="VARCHAR(10)"/&gt;
        &lt;/addColumn&gt;
        
        &lt;rollback&gt;
            &lt;dropColumn tableName="employees" columnName="temp_access_code"/&gt;
        &lt;/rollback&gt;
    &lt;/changeSet&gt;

&lt;/databaseChangeLog&gt;
</code></pre>
<p>Add this to your master file and run the application. The column is added.</p>
<p>If you were deploying this via a CI/CD pipeline and the deployment failed, you could trigger a Liquibase Maven command to roll back by a specific number of steps (for example, <code>mvn liquibase:rollback -Dliquibase.rollbackCount=1</code>), or roll back to a specific tag we discussed earlier.</p>
<h3 id="heading-the-reality-check-on-rollbacks">The Reality Check on Rollbacks</h3>
<p>While it's important to know how rollbacks work, here's a practical reality from the trenches of backend engineering: <strong>Rollbacks are often discussed but rarely executed cleanly in production.</strong></p>
<p>Dropping a column is mathematically easy. Recovering the customer data that was written to that column during the 15 minutes the bad code was live is incredibly difficult.</p>
<p>Because of this, modern engineering teams often prefer a "roll forward" strategy. If a migration causes an issue, instead of running a scary database rollback command, they quickly write a new changeSet that fixes the issue (for example, adding a missing index or relaxing a constraint) and deploy the application again.</p>
<p>It's highly recommended to design your database changes to be additive and non-destructive to avoid needing complex rollbacks in the first place.</p>
<h2 id="heading-common-beginner-mistakes">Common Beginner Mistakes</h2>
<p>Adopting database version control is a massive step forward for any engineering team, but it comes with a learning curve. When developers transition from writing loose SQL scripts to using Liquibase, they tend to fall into a few predictable traps.</p>
<p>Here are the most common beginner mistakes and exactly how to avoid them.</p>
<h3 id="heading-1-the-mega-changeset">1. The "Mega" ChangeSet</h3>
<p>When starting out, it's tempting to dump your entire initial schema into a single XML file under a single <code>changeSet</code>. You might put 15 <code>createTable</code> statements and 20 <code>addForeignKeyConstraint</code> statements into <code>id="1"</code>.</p>
<p>This is a terrible idea for one simple reason: transaction failure.</p>
<p>If your database engine fails on table number 14 (perhaps due to a syntax error), what happens to the first 13 tables? Some database engines support transactional DDL (Data Definition Language), meaning it will roll back all 13 tables automatically. But many databases do not.</p>
<p>If it fails halfway through, your database is now in a fractured state. Liquibase didn't record <code>id="1"</code> as successful, so the next time you start the app, it will try to create all 15 tables again. It will immediately crash because table 1 already exists.</p>
<p><strong>The Fix:</strong> Stick to the rule of "one logical operation per changeSet." If you're creating three tables, write three separate changeSets. If one fails, the successful ones are permanently recorded, and you only have to fix the broken one.</p>
<h3 id="heading-2-manual-database-tweaking-the-phantom-menace">2. Manual Database Tweaking (The Phantom Menace)</h3>
<p>This is the most dangerous habit to break. A developer spots a missing index in production. Instead of writing a Liquibase migration, going through code review, and deploying, they log directly into the production database and run <code>CREATE INDEX</code> manually to save time.</p>
<p>A week later, another developer writes a proper Liquibase migration to create that exact same index and deploys it. The application crashes on startup. Liquibase tries to execute the <code>CREATE INDEX</code> command, but the database throws an error saying the index already exists.</p>
<p>When you adopt Liquibase, you must accept a fundamental rule: <strong>Liquibase is the absolute source of truth for your schema.</strong> Human hands should never touch the database structure directly.</p>
<p><strong>The Fix:</strong> If someone accidentally does this, you have two options to fix the deployment pipeline. You can manually drop the index from the database so Liquibase can recreate it properly, or you can use the <code>&lt;preConditions&gt;</code> tag in Liquibase to check if the index exists before trying to create it.</p>
<h3 id="heading-3-ignoring-the-from-scratch-build">3. Ignoring the "From Scratch" Build</h3>
<p>When you work on a project for months, your local database accumulates a lot of history. You write migrations assuming certain tables or test data already exist.</p>
<p>Then, a new developer joins the team. They pull the code, spin up an empty database, start Spring Boot, and the migrations crash halfway through.</p>
<p>This happens because the migrations rely on an assumed state (like expecting a specific row to exist before creating a foreign key) rather than a guaranteed state.</p>
<p><strong>The Fix:</strong> You should regularly test your migrations against a completely blank database. If you're using Docker, tear down your database container and rebuild it. If you're using a file-based H2 database like we set up earlier, simply delete the <code>./data/employeedb.mv.db</code> file from your project folder and restart Spring Boot. If the application can't boot successfully from a completely empty state, your migration history is broken.</p>
<h3 id="heading-4-hardcoding-environment-details">4. Hardcoding Environment Details</h3>
<p>Beginners sometimes hardcode environment-specific details directly into their XML files. For example, they might hardcode a specific schema name (schemaName="dev_schema") or grant permissions to a specific local user (GRANT ALL ON employees TO my_local_user).</p>
<p>When this code goes to staging, the staging database uses a different schema name, and the deployment fails.</p>
<p>The Fix: Keep your migrations abstract. Let Spring Boot handle the connection details via application.properties. If you absolutely must use dynamic values inside your Liquibase files, use property substitution. You can define variables in Liquibase and pass them in from Spring Boot during startup.</p>
<h3 id="heading-5-messing-up-migration-ordering">5. Messing Up Migration Ordering</h3>
<p>Liquibase executes files in the exact order they're listed in your <code>db.changelog-master.xml</code> file.</p>
<p>If developer A creates the <code>departments</code> table in a branch, and developer B creates a foreign key linking to <code>departments</code> in another branch, whoever merges their code first dictates the order. If developer B's code gets included in the master file <em>before</em> developer A's code, Liquibase will try to create the foreign key before the target table exists.</p>
<p><strong>The Fix:</strong> The master changelog is the ultimate chokepoint for database changes. During code reviews, always verify that the <code>&lt;include&gt;</code> statements are ordered chronologically and that dependencies make sense.</p>
<h2 id="heading-liquibase-vs-flyway-vs-manual-sql-scripts">Liquibase vs Flyway vs Manual SQL Scripts</h2>
<p>When you decide to implement database version control, you'll immediately face a choice. Liquibase isn't the only tool in the Java ecosystem. The three most common approaches to managing schema evolution are Liquibase, Flyway, and manual SQL scripts.</p>
<p>You should understand the practical tradeoffs of each so you can choose the right tool for your specific team and project.</p>
<h3 id="heading-1-manual-sql-scripts-the-baseline">1. Manual SQL Scripts (The Baseline)</h3>
<p>This is the default approach for most beginners. You write a script.sql file and execute it directly against the database using a tool like DBeaver, pgAdmin, or DataGrip.</p>
<ul>
<li><p><strong>Strengths:</strong> There is zero setup required. You have total control over the exact syntax, and every backend developer already knows how to write SQL.</p>
</li>
<li><p><strong>Weaknesses:</strong> There's absolutely no execution tracking. This approach practically guarantees schema drift across environments. Deployments become stressful because they rely on humans remembering to execute the right scripts in the exact right order.</p>
</li>
<li><p><strong>The Verdict:</strong> Manual scripts are perfectly fine for solo weekend projects or rapid prototyping where you don't care if the database gets destroyed. But they become a massive liability the moment a second developer joins the team or a staging environment is created.</p>
</li>
</ul>
<h3 id="heading-2-flyway-the-sql-purist">2. Flyway (The SQL Purist)</h3>
<p>Flyway is the most popular alternative to Liquibase. Instead of using XML or YAML abstractions, Flyway embraces raw SQL. You write pure SQL files with a strict naming convention (for example, V1__Create_employee_table.sql).</p>
<ul>
<li><p><strong>Strengths:</strong> There's no new syntax to learn. If you know SQL, you already know how to use Flyway. It's incredibly fast to set up, highly opinionated, and integrates flawlessly with Spring Boot.</p>
</li>
<li><p><strong>Weaknesses:</strong> Because you write raw SQL, your migrations are intimately tied to your specific database dialect. If you write Flyway scripts for MySQL and later decide to migrate the project to PostgreSQL, you have to manually rewrite your migration history. Furthermore, seamless automated rollbacks are a paid feature in Flyway's commercial tier.</p>
</li>
<li><p><strong>The Verdict:</strong> Flyway is excellent for teams that are highly skilled in SQL, are permanently committed to a single database vendor, and prefer strict conventions over flexible configurations.</p>
</li>
</ul>
<h3 id="heading-3-liquibase-the-abstraction-layer">3. Liquibase (The Abstraction Layer)</h3>
<p>As we have seen throughout this tutorial, Liquibase takes a different approach by abstracting database changes into XML, YAML, or JSON.</p>
<ol>
<li><p><strong>Strengths:</strong> It's truly database-agnostic. You define the logical structure, and Liquibase automatically translates that into the correct SQL dialect for H2, PostgreSQL, or Oracle. It supports powerful automatic rollbacks, preconditions, contexts, and deployment labels out of the box for free.</p>
</li>
<li><p><strong>Weaknesses:</strong> It has a steeper learning curve than Flyway. The XML syntax is undeniably verbose and can feel heavy for very simple, single-table applications.</p>
</li>
<li><p><strong>The Verdict:</strong> Liquibase shines in complex applications, multi-tenant systems, projects that support multiple database vendors, and enterprise environments that require fine-grained control over CI/CD deployment pipelines.</p>
</li>
</ol>
<h2 id="heading-liquibase-best-practices">Liquibase Best Practices</h2>
<p>Now that you understand the mechanics of Liquibase, you need to know how to use it in a professional environment. Writing a migration that works on your local machine is only half the battle. Writing a migration that your entire team can safely deploy to production requires discipline.</p>
<p>Here are the engineering best practices you should adopt when managing database changes.</p>
<h3 id="heading-1-one-logical-change-per-changeset-the-atomic-rule">1. One Logical Change Per ChangeSet (The Atomic Rule)</h3>
<p>We discussed this in the common mistakes section, but it's important enough to repeat. Never bundle a table creation, an index creation, and a data insertion into a single changeSet.</p>
<p>If you're adding a salary column and an idx_employee_salary index, put them in two separate changeSets within the same file. This ensures that if the index creation fails, the column creation is still safely recorded, and you don't end up in a fractured database state.</p>
<h3 id="heading-2-meaningful-file-organization-and-naming">2. Meaningful File Organization and Naming</h3>
<p>Don't name your files <code>update1.xml</code> or <code>new_changes.xml</code>. Your file names should tell a story about how your database evolved.</p>
<p>Adopt a strict prefix system. In our project, we used <code>01-create-employees.xml</code> and <code>02-add-employee-email.xml</code>. In a real team, you might use Jira ticket numbers or release versions (for example, <code>v1.2.0_ticket-482_add_email.xml</code>). Whatever convention you choose, enforce it rigorously during code reviews.</p>
<h3 id="heading-3-treat-database-changes-like-application-code">3. Treat Database Changes Like Application Code</h3>
<p>Database migrations belong in source control right next to your Java code. They should be reviewed with the exact same level of scrutiny.</p>
<p>When reviewing a pull request that includes a Liquibase file, engineers should ask:</p>
<ul>
<li><p>Does this column need an index?</p>
</li>
<li><p>Is this a destructive change (like renaming a column) that will break the currently running application?</p>
</li>
<li><p>Did the author include explicit rollback instructions for custom SQL?</p>
</li>
</ul>
<h3 id="heading-4-integrate-migrations-into-cicd">4. Integrate Migrations into CI/CD</h3>
<p>Human hands should never run database migrations against a production server. Your deployment pipeline should handle this automatically.</p>
<p>When you merge code into your main branch, your CI/CD pipeline (like GitHub Actions or GitLab CI) should build your Spring Boot application and deploy it. Because we bundled Liquibase into our Spring Boot startup sequence, the application will automatically migrate the production database before it starts accepting web traffic.</p>
<p>Here's what a safe, automated deployment pipeline looks like:</p>
<img src="https://cdn.hashnode.com/uploads/covers/61c1acb4a90dea775da8262b/7a9323e6-c44a-4ee5-8307-3ee8a696c3a9.png" alt="CI/CD pipeline architecture showing code moving from Git to testing, deploying to a staging environment where Liquibase runs, and then promoting to production." style="display:block;margin:0 auto" width="677" height="657" loading="lazy">

<p>In a mature deployment pipeline, human hands never touch the production database. When you merge a pull request, the CI/CD pipeline builds the code and runs unit tests. It deploys the Spring Boot application to a staging environment, where Liquibase automatically acquires a lock and runs the migrations during startup. Once validated, that exact same artifact is promoted to production, triggering the identical automated migration process.</p>
<h3 id="heading-5-never-fix-forward-by-deleting-history">5. Never Fix Forward by Deleting History</h3>
<p>If a migration fails in an upper environment (like staging or production), never log into the database to delete the <code>DATABASECHANGELOG</code> row so you can try again.</p>
<p>You must respect the immutability of the changelog. If you made a mistake, write a new changeSet that drops the broken table or fixes the data type, and push it through your Git workflow just like you would a Java bug fix.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Managing database schema changes doesn't have to be a source of anxiety.</p>
<p>By treating your database schema as code, you eliminate the chaos of manual SQL scripts. You prevent the dreaded "schema drift" where every developer's local machine behaves differently. Most importantly, you make your deployments predictable and boring (which is exactly what you want deployments to be).</p>
<p>In this tutorial, you built a practical Spring Boot application from scratch. You learned how Liquibase intercepts the application startup, locks the database, calculates cryptographic checksums, and safely applies incremental changes. You evolved a single table into a relational schema, added seed data, and learned how to avoid the most common traps beginners fall into.</p>
<p>The next time you start a Spring Boot project, don't reach for a manual SQL client. Add the Liquibase dependency, create your master changelog, and start version controlling your database from day one. Your future self (and your team) will thank you.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ RAG Explained Simply with a Real Project ]]>
                </title>
                <description>
                    <![CDATA[ If you have used ChatGPT, you know how magical it feels. You ask a question, and it instantly generates a highly articulate answer. But you also probably know its biggest flaw. If you ask it about you ]]>
                </description>
                <link>https://www.freecodecamp.org/news/rag-explained-simply-with-a-real-project/</link>
                <guid isPermaLink="false">6a186a9260295e5547e04628</guid>
                
                    <category>
                        <![CDATA[ RAG  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ llm ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Thu, 28 May 2026 16:17:22 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/5dc3370a-a536-43f6-850e-223928f99870.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you have used ChatGPT, you know how magical it feels. You ask a question, and it instantly generates a highly articulate answer.</p>
<p>But you also probably know its biggest flaw. If you ask it about your company's internal code, your private Notion workspace, or an event that happened yesterday, it fails.</p>
<p>Usually, it does one of two things. It either apologizes and says it doesn't have access to that information, or worse, it confidently makes something up entirely.</p>
<p>This happens because Large Language Models (LLMs) are like extremely smart students who are locked in a room without internet access. They only know what they memorized before they were locked inside. If you ask them a question outside of their memorized knowledge, they have to guess.</p>
<p>So, how do we fix this? How do we get an AI to answer questions about our private data without retraining the entire model from scratch?</p>
<p>The answer is <strong>RAG</strong>, which stands for Retrieval-Augmented Generation.</p>
<p>RAG is the architecture behind nearly every modern AI application that interacts with private data. If you have ever used a "chat with PDF" app or a customer support bot that actually knows company policies, you have interacted with RAG.</p>
<p>In this article, we'll break down exactly how RAG works from first principles. Then, we'll build a working RAG application from scratch using Python.</p>
<h3 id="heading-heres-what-well-cover">Here's what we'll cover:</h3>
<ul>
<li><p><a href="#heading-what-is-rag">What is RAG?</a></p>
</li>
<li><p><a href="#heading-why-traditional-llms-fail">Why Traditional LLMs Fail</a></p>
</li>
<li><p><a href="#heading-how-rag-works-internally">How RAG Works Internally</a></p>
</li>
<li><p><a href="#heading-how-to-build-a-real-rag-project">How to Build a Real RAG Project</a></p>
</li>
<li><p><a href="#heading-the-full-data-flow">The Full Data Flow</a></p>
</li>
<li><p><a href="#heading-common-rag-problems">Common RAG Problems</a></p>
</li>
<li><p><a href="#heading-advanced-rag-concepts">Advanced RAG Concepts</a></p>
</li>
<li><p><a href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-what-is-rag">What is RAG?</h2>
<p>RAG stands for <strong>Retrieval-Augmented Generation</strong>. Let's break down what those three words actually mean.</p>
<ul>
<li><p><strong>Retrieval:</strong> Finding relevant information from a database.</p>
</li>
<li><p><strong>Augmented:</strong> Adding that information to the user's original question.</p>
</li>
<li><p><strong>Generation:</strong> Asking the LLM to write an answer using only the added information.</p>
</li>
</ul>
<h3 id="heading-the-open-book-test-analogy">The Open-Book Test Analogy</h3>
<p>To build a mental model, think of a traditional LLM as a student taking a closed-book exam. The student has read billions of books in the past, but right now, they have to answer questions purely from memory. Sometimes they forget facts, and sometimes they make up answers to avoid leaving the page blank. Not gonna lie, I pulled the same move in quite a few university exams.</p>
<p>RAG turns this into an open-book exam.</p>
<p>When you ask a question, the system first runs to a massive library (your database), finds the exact pages that contain the answer, and hands those pages to the student. The student then reads those specific pages and writes a perfect answer.</p>
<p>Instead of relying on the AI's memory, we're only relying on its reading comprehension skills.</p>
<h2 id="heading-why-traditional-llms-fail">Why Traditional LLMs Fail</h2>
<p>Before we dive into how to build RAG, we need to understand exactly why prompting an LLM on its own isn't enough.</p>
<ol>
<li><p><strong>Training cutoffs:</strong> Training an LLM takes months and costs millions of dollars. Because of this, models are trained on data up to a specific date. If an LLM was trained in 2025, it has absolutely no idea what happened in 2026.</p>
</li>
<li><p><strong>No access to private data:</strong> Your company's Jira tickets, internal wikis, and Slack messages are private. OpenAI, Google, and Anthropic don't have them in their training datasets.</p>
</li>
<li><p><strong>Hallucinations:</strong> LLMs are essentially advanced autocomplete engines. They predict the next most likely word based on patterns. If they don't know a fact, they'll string together words that sound highly plausible but may be completely incorrect. We call this hallucinating.</p>
</li>
<li><p><strong>Context window limitations:</strong> You might be thinking, "Why not just copy and paste my entire company wiki into the ChatGPT prompt?" Well, every LLM has a "context window", which is the maximum amount of text it can process at once. Even with modern models that have massive context windows, pasting thousands of documents into a prompt is incredibly slow and expensive. Also, models tend to lose track of information when you overwhelm them with too much text.</p>
</li>
<li><p><strong>The high cost of retraining:</strong> You could theoretically fine-tune an LLM on your private data. But fine-tuning is complicated and expensive. More importantly, knowledge changes constantly. If you update a company policy, you would have to fine-tune the model all over again to teach it the new rule.</p>
</li>
</ol>
<p>RAG solves all of these problems. It gives the LLM access to real-time, private data without needing to retrain the model.</p>
<h2 id="heading-how-rag-works-internally">How RAG Works Internally</h2>
<p>To make RAG work, we need a specific pipeline of technologies. Let's explore every major concept in the RAG architecture.</p>
<h3 id="heading-documents">Documents</h3>
<p>Everything starts with your raw data. These are your PDFs, database records, text files, or scraped websites. In the AI world, we refer to all of these source materials generally as "documents".</p>
<h3 id="heading-chunking">Chunking</h3>
<p>You can't feed a 500-page book into an AI all at once for a simple question. It's inefficient. Instead, we break the documents down into smaller, manageable pieces called "chunks". A chunk might be a single paragraph or a few sentences.</p>
<p>This matters because when a user asks a question, we only want to retrieve the specific paragraphs that contain the answer, not the entire book. If we skipped chunking, the system would retrieve massive walls of text, which would crash the LLM's context window.</p>
<h3 id="heading-embeddings">Embeddings</h3>
<p>This is the most intimidating term for beginners, but the concept is brilliant. Computers don't understand words, but they're great at math. <strong>Embeddings</strong> are a way to translate human language into lists of numbers (vectors) that capture the actual meaning of the text.</p>
<p>Imagine a 2D map. We can plot the word "Dog" at coordinates [2, 3] and the word "Puppy" at [2.1, 3.1]. Even though they're different words, the computer knows they mean similar things because their coordinates are physically close together on the map. The word "Car" might be way over at [10, 10].</p>
<p>In a real AI system, an embedding model doesn't use just 2 dimensions. It maps sentences across thousands of dimensions to capture deep semantic meaning.</p>
<h3 id="heading-vector-databases">Vector Databases</h3>
<p>Once we convert all of our text chunks into number coordinates (embeddings), we need a place to store them. Traditional SQL databases are great at finding exact keyword matches. But they're terrible at finding "similar meanings".</p>
<p>A <strong>vector database</strong> is specifically designed to store lists of numbers and quickly calculate the distance between them. Popular vector databases include ChromaDB, Pinecone, Weaviate, FAISS, and Milvus.</p>
<h3 id="heading-semantic-search-and-similarity-matching">Semantic Search and Similarity Matching</h3>
<p>When a user types a question into our chatbot, we run the question through the exact same embedding model. The question becomes a list of numbers.</p>
<p>We then ask the vector database to perform a <strong>similarity search</strong>. The database looks at the coordinates of the user's question and finds the stored chunks that are located closest to it in mathematical space. Because distance equals meaning, the closest chunks will contain the most relevant information to answer the question.</p>
<h3 id="heading-prompt-augmentation">Prompt Augmentation</h3>
<p>Now we have the user's original question and the text chunks we retrieved from the database. We "augment" (add to) the prompt. We create a hidden template behind the scenes that looks like this:</p>
<blockquote>
<p>"You are a helpful assistant. Use ONLY the following context to answer the user's question.</p>
<p>Context:</p>
<p>[Insert retrieved chunks here]</p>
<p>Question:</p>
<p>[Insert user question here]"</p>
</blockquote>
<h3 id="heading-final-llm-response">Final LLM Response</h3>
<p>We send this giant, augmented prompt to the LLM. The LLM reads the context, processes the question, and generates a factual response based entirely on the provided data.</p>
<h3 id="heading-quick-recap">Quick Recap</h3>
<p>A RAG pipeline usually looks like this:</p>
<img src="https://cdn.hashnode.com/uploads/covers/61c1acb4a90dea775da8262b/fa6b3432-bc29-4346-8537-3f5b3861b9d1.png" alt="RAG Pipeline" style="display:block;margin:0 auto" width="795" height="1310" loading="lazy">

<h2 id="heading-how-to-build-a-real-rag-project">How to Build a Real RAG Project</h2>
<p>Let's build a real-world RAG application. We'll build an AI chatbot that reads and understands a PDF document.</p>
<p>To make this completely free to build, we'll use Python, LangChain (a popular AI framework), Google's Gemini API (which has a generous free tier for developers), and ChromaDB (a local vector database).</p>
<p>Note: We'll be using the free Gemini tier here for illustration purposes so you can learn without spending money. Because LangChain is modular, you can easily swap this out for any other production-grade model later just by changing one line (or a few lines) of code.</p>
<h3 id="heading-project-setup">Project Setup</h3>
<p>First, open your terminal or command prompt, create a new directory for your project, and navigate into it:</p>
<pre><code class="language-shell">mkdir my-rag-project
cd my-rag-project
</code></pre>
<p>Next, it's a best practice to create an isolated <strong>virtual environment</strong>. This ensures that the packages we install for this project don't conflict with other Python projects on your computer.</p>
<p>To create and activate a virtual environment, run the commands for your specific operating system:</p>
<p><strong>For macOS and Linux:</strong></p>
<pre><code class="language-shell">python3 -m venv venv
source venv/bin/activate
</code></pre>
<p><strong>For Windows (Command Prompt):</strong></p>
<pre><code class="language-shell">python -m venv venv
venv\Scripts\activate
</code></pre>
<p><strong>For Windows (PowerShell):</strong></p>
<pre><code class="language-shell">python -m venv venv
.\venv\Scripts\Activate.ps1
</code></pre>
<p>Once activated, you'll see <code>(venv)</code> appear at the beginning of your terminal line. Now, go ahead and install the required libraries inside your fresh environment:</p>
<pre><code class="language-shell">python -m pip install --upgrade pip
pip install langchain langchain-google-genai langchain-community chromadb python-dotenv pypdf
</code></pre>
<p>You'll also need a Google Gemini API key. You can get one for free from <a href="https://aistudio.google.com/app/api-keys">Google AI Studio</a>.</p>
<p>Instead of running messy terminal configuration commands for different operating systems, create a new file named <code>.env</code> in the root of your project folder and add your key like this:</p>
<pre><code class="language-plaintext">GOOGLE_API_KEY=your_actual_api_key_here
</code></pre>
<h3 id="heading-preparing-the-pdf">Preparing the PDF</h3>
<p>Since this is a "Chat with PDF" project, you’ll need a sample PDF document to work with. To keep things simple, download <a href="https://drive.google.com/file/d/1UOUVl2mzc39SEHxpi8hujpueIyhEUPC7/view?usp=sharing">this ready-made sample document</a> below and place it inside your project folder.</p>
<p>You can then use this PDF throughout the tutorial for testing uploads, parsing, embeddings, and chat functionality.</p>
<h3 id="heading-writing-the-rag-code-step-by-step">Writing the RAG Code Step-by-Step</h3>
<p>Create a Python file named <code>rag_app.py</code> in your project folder. Instead of copying a massive block of code, we'll build this application block by block so we can understand exactly how data flows through our pipeline.</p>
<h4 id="heading-step-1-imports-and-environment-setup">Step 1: Imports and Environment Setup</h4>
<p>At the very top of your file, add the necessary library imports and initialize your environment configuration:</p>
<pre><code class="language-python">import os
from dotenv import load_dotenv
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_google_genai import GoogleGenerativeAIEmbeddings, ChatGoogleGenerativeAI
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnablePassthrough

# Load environment variables from the .env file
load_dotenv()
</code></pre>
<p>We're bringing in LangChain modules to handle loading, splitting, embedding, storing, and prompting. The <code>load_dotenv()</code> function is mandatory because it scans our <code>.env</code> file and loads the <code>GOOGLE_API_KEY</code> into our system's background environment variables, ensuring our AI models can authenticate seamlessly without hardcoding passwords.</p>
<h4 id="heading-step-2-loading-the-pdf-document">Step 2: Loading the PDF Document</h4>
<p>Next, let's point our script to the PDF document we downloaded earlier:</p>
<pre><code class="language-python">print("Loading PDF document...")
loader = PyPDFLoader("TechCorp_Official_Employee_Handbook.pdf")
document = loader.load()

print(document[0].page_content)
</code></pre>
<p>Computers can't read a PDF like a standard text file because PDFs contain complex layout streams. <code>PyPDFLoader</code> handles the heavy lifting of opening the file, stripping away visual layout formatting, and extracting the raw text characters into a clean format that LangChain can work with.</p>
<p>At this point, when you run the script, you should see the text content from the first page of the PDF printed in the terminal. This is a quick way to verify that the PDF was loaded successfully and that <code>PyPDFLoader</code> was able to extract readable text from the document correctly.</p>
<h4 id="heading-step-3-chunking-the-text">Step 3: Chunking the Text</h4>
<p>Now that the raw text is in memory, we need to chop it up into smaller pieces:</p>
<pre><code class="language-python">print("Chunking text...")
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50
)
chunks = text_splitter.split_documents(document)

print(chunks[0].page_content)
</code></pre>
<p>If a user asks a simple question, sending an entire 100-page document to the LLM is incredibly slow and expensive. <code>RecursiveCharacterTextSplitter</code> cuts the text into segments of roughly 500 characters.</p>
<p>The <code>chunk_overlap=50</code> parameter tells the text splitter to repeat the last 50 characters of one chunk at the beginning of the next. This helps preserve context between chunks so that sentences or ideas are not abruptly cut off.</p>
<p>Without overlap, important information near chunk boundaries could be separated, making retrieval less accurate. By maintaining a small shared section between neighboring chunks, the model can better understand continuity in the document, resulting in more reliable search results and higher-quality responses.</p>
<p>When you run the script, you should now see the contents of the first text chunk printed in the terminal.</p>
<h4 id="heading-step-4-creating-embeddings-and-initializing-the-vector-db">Step 4: Creating Embeddings and Initializing the Vector DB</h4>
<p>With our chunks ready, we'll convert them into vector coordinates and save them locally:</p>
<pre><code class="language-python">print("Creating vector database...")
embeddings = GoogleGenerativeAIEmbeddings(model="models/embedding-001")
vector_db = Chroma.from_documents(
    documents=chunks, 
    embedding=embeddings, 
    persist_directory="./chroma_db"
)
</code></pre>
<p>This is the mathematical core of RAG. <code>GoogleGenerativeAIEmbeddings</code> takes a raw text chunk and turns it into a list of numbers representing its conceptual meaning. We then hand those chunks and numbers to <code>Chroma</code>, which maps them into a local database directory named <code>chroma_db</code> on your hard drive, allowing for lightning-fast mathematical lookups later.</p>
<h4 id="heading-step-5-setting-up-the-retriever-and-prompt-template">Step 5: Setting Up the Retriever and Prompt Template</h4>
<p>Now we need a mechanism to query our database and a structure to house our instructions:</p>
<pre><code class="language-python"># Configure the database to act as a document retriever
retriever = vector_db.as_retriever(search_kwargs={"k": 2})

# Define the hidden prompt structure for the LLM
template = """
Use the following pieces of retrieved context to answer the question. 
If you don't know the answer, just say that you don't know. 
Use three sentences maximum and keep the answer concise.

Context: {context}

Question: {question}

Answer:
"""
prompt = PromptTemplate.from_template(template)
</code></pre>
<p><code>vector_db.as_retriever()</code> converts the vector database into a retriever object that can search through stored document embeddings and return the most relevant chunks for a user’s question. Setting <code>k=2</code> on our retriever tells the database to only pull the top two most relevant chunks for any given question, which keeps things clean and efficient.</p>
<p>The prompt template acts as hidden instructions for the model. When a user asks a question, LangChain automatically replaces <code>{context}</code> with the retrieved document chunks and <code>{question}</code> with the user’s actual query. The template also acts as a safety guardrail. By explicitly telling the model to say "I don't know" if the context lacks information, we heavily suppress the model's tendency to hallucinate fake answers.</p>
<h4 id="heading-step-6-initializing-the-llm-and-constructing-the-rag-chain">Step 6: Initializing the LLM and Constructing the RAG Chain</h4>
<p>Next, we hook up our language model and construct our execution pipeline:</p>
<pre><code class="language-python"># Initialize the free Gemini model tier
llm = ChatGoogleGenerativeAI(model="gemini-1.5-flash", temperature=0)

# Helper function to stitch retrieved chunks into a single text block
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

# Connect everything together using LangChain Expression Language (LCEL)
rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
)
</code></pre>
<p>We use <code>gemini-3.5-flash</code> with a <code>temperature=0</code> setting to force the model to be completely factual and analytical rather than creative.</p>
<p>The retriever returns multiple document chunks as structured objects. The <code>format_docs</code> function converts those chunks into a single continuous text block by joining their <code>page_content</code>. This step is necessary because the prompt expects a clean, readable context string rather than a list of document objects.</p>
<p>Finally, we connect everything using LangChain Expression Language (LCEL). When a question comes in, it passes it to the retriever, formats the resulting text documents, passes the filled template to the prompt handler, and pushes the final product straight to the LLM.</p>
<h4 id="heading-step-7-invoking-the-chain-with-a-question">Step 7: Invoking the Chain with a Question</h4>
<p>Finally, let's execute the pipeline and print the result out to the console:</p>
<pre><code class="language-python">user_question = "What days can I work from home?"
print(f"\nQuestion: {user_question}")

response = rag_chain.invoke(user_question)
print(f"Answer: {response.content}")
</code></pre>
<p>This is where the magic happens. The <code>invoke</code> command sets off the entire chain reaction we just built. When you run this, the console will output:</p>
<pre><code class="language-shell">Loading PDF document...
Chunking text...
Creating vector database...

Question: What days can I work from home?
Answer: [{'type': 'text', 'text': 'You are permitted to work from home on Tuesdays and Thursdays. Additional remote flexibility may also be approved by your department manager.', 'extras': {'signature': 'Eo0JCooJAQw51seue7vZT7Vby90GMDLhtOBWLKm5UjfEro7f8dRoKC0KAIHxSqQSLXq0s3kf6yfzTsgaUMFiNd0fnwtNSNoApzcZ7huRD8iq+f+xomoXGhmFYClnLApHUKtOLykICluJnM1j6DfYGaVHKLqU0MF4+Fng9CdqXVqPgN9HcfJEvSpeMAc9vTYENj07s8N6MidlMvMt1w0fl4GCjxAZXyEngdU4kGfjUqaKyjjCQ9yLFeoXrV55pqZdkElLxXEK4ZWNnMGh5NDqGmt2b0kMG4KoCdunUltBr1ctV15rZ+724T0qnjDvI+pIgp/ZtKa423gaVXSkSmdvSePEog38blJ2dgjtZg72XF5xlh45Yv06fZVu7e60ZB1sTn4W8iWuYGQ61i/xCN6xCX/e3SuitjwQoHSlEe/iuoaNf5BXhdp87TUyQTawiY+qIZjgWz2AMLUbMcOvns/0iFt6jpUkXr/dO4eYF39UCosrbWC5TZQp2gllNQ6mlrczTAKqe8mPZwmBVuTJ3kx3q+SsVROln584EdD94IxXrgLXhuLkbR9ub0qyvjBfAmIfvUEK5pcaBCGydQvheH9wsIvAOG1kspMb/wqjAv/mpmii8J9vztSvM9PR9v7L3YLu8vcANol80w2PfeHhyWUJWit8R58kKd7HHor5GJhA436x+tCukIlBq2oTcob+ydxVJydA12pRsiuw4kYkEIU8nr5yCiIwjYCDtVm6Ws0RUnhyk5u+dRONPZ6g+mfBShKCnahcIMzzJpXznmPXvmP2C96uD64SGTI6L86EMlLEz06/cTJTabgqAYqe2AhERgnYc/4d0XabQOkzvDmBKMr5/LOAt3ZZg7X4PIuefEwxx0eB60gLROefcbbu8k+KPazqFsDP/YA/aPyAxyss/6V43EID0amJcDA81LKJzazL9KnclefQZrN9viIwteMaV04IIlx+Ynk1vZi/LVgWiFuDVWF3Ql2luY4KwFpfFDxQ728gkrhvUdTBrfUeKRSLV1W4ox6I7ogo0e9i7db2lkOQljctGs3Km3hWu4JOkH+YzLNmcDHMF3imfgQH5Ml99H9PXh1ScBjq47MXKzJPdHijkY5ZRSjceEIlKEGv8afQO60NB8lk1MQAGwd+CxqIwVg11N8q9EFSwdJmVVmoyM1nINGJERSKhKOrkqBsOELfpKDjv14tuNgDUy4wdtuxn8C4tJBKvN8t/hrW/Z65VoBGdMwA08sRSV6Fp5l/gSdYeB9yA/Lx/VGkgVqaP5tU73XrE/XO8ysJ/kgRDXiTvsg+2uayU1Q9PfKFAawopslwybCHtdOwaVgsRdA5R4f1NIkPoP/sX+iBxyR0kKg6v4RRAj851WifM2fQ8Vsw5dtFSeh/4TfYg1GCCCDNT4JwrtI8fqcF+qMQqUb+oUqoyzjzFqqSRxXcyqHXOLV9V9C6yWYmZ3TSY043WL9L4kGGJGxFHD5VWG77Quiy+rHWGO13LOc5EBKIO05sg1xnI88QQTUgkxwJeuntytIy3f3pfMVrFYFkvi8w5LzL4RK68+4HMg=='}}]
</code></pre>
<p>Modern LLMs like Google's Gemini are <strong>multimodal</strong>. This means they're designed to read and generate not just plain text, but images, video, and audio simultaneously. Because of this, the LangChain Google integration doesn't always return a simple text string. Instead, it returns a <strong>list of content blocks</strong>.</p>
<p>In your output, the AI successfully returned your text, but it also included an <code>extras</code> dictionary containing a <code>signature</code>. This signature is a behind-the-scenes data point used by Google for AI safety tracking, grounding metadata, and thought-process verification.</p>
<p>To get a clean, human-readable string, you simply need to extract the <code>text</code> value from that list. You can update your final print statement to check if the response is a list and extract the text automatically:</p>
<pre><code class="language-python"># Clean up the output if Gemini returns a list of content blocks
if isinstance(response.content, list):
    clean_answer = response.content[0]['text']
else:
    clean_answer = response.content

print(f"Answer: {clean_answer}")
</code></pre>
<p>Now, your output will look like this:</p>
<pre><code class="language-shell">Question: What days can I work from home?
Answer: You are permitted to work from home on Tuesdays and Thursdays. Additional remote flexibility may also be approved by your department manager.
</code></pre>
<h4 id="heading-step-8-making-it-conversational">Step 8: Making it Conversational</h4>
<p>Right now, our script hardcodes a single question, prints the answer, and immediately exits. In the real world, you want to chat with your documents naturally. Let's upgrade our script to run continuously in your terminal so you can ask as many questions as you want without restarting the program.</p>
<p>Replace the bottom section of your code with a simple <code>while</code> loop:</p>
<pre><code class="language-python"># Chat with your PDF in a continuous loop
print("\n--- PDF Chatbot Initialized ---")
print("Type 'exit' or 'quit' to stop.")

while True:
    # 1. Wait for the user to type a question
    user_question = input("\nYour Question: ")

    # 2. Allow the user to break the loop and close the program
    if user_question.lower() in ['exit', 'quit']:
        print("Shutting down chatbot. Goodbye!")
        break

    # 3. Send the question through our RAG chain
    response = rag_chain.invoke(user_question)

    # 4. Clean up the output format
    if isinstance(response.content, list):
        clean_answer = response.content[0]['text']
    else:
        clean_answer = response.content

    # 5. Print the final answer to the console
    print(f"Answer: {clean_answer}")
</code></pre>
<p>By using Python's <code>input()</code> function wrapped inside an infinite <code>while True</code> loop, we keep the Python script alive. The PDF chunks and vector database stay loaded in your computer's memory, allowing you to fire off consecutive questions instantly. This transforms your script from a static demonstration into a fully interactive AI tool!</p>
<p>Here's a sample run:</p>
<img src="https://cdn.hashnode.com/uploads/covers/61c1acb4a90dea775da8262b/987055ad-353d-4bd3-9026-6e4172a0904a.png" alt="Image of sample run" style="display:block;margin:0 auto" width="1907" height="994" loading="lazy">

<h4 id="heading-full-code">Full Code</h4>
<pre><code class="language-python">import os
from dotenv import load_dotenv
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_google_genai import GoogleGenerativeAIEmbeddings, ChatGoogleGenerativeAI
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import PromptTemplate
from langchain_core.runnables import RunnablePassthrough

# Load environment variables from the .env file
load_dotenv()

print("Loading PDF document...")
loader = PyPDFLoader("TechCorp_Official_Employee_Handbook.pdf")
document = loader.load()
# print(document[0].page_content)

print("Chunking text...")
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50
)
chunks = text_splitter.split_documents(document)
# print(chunks[0].page_content)

print("Creating vector database...")
embeddings = GoogleGenerativeAIEmbeddings(model="gemini-embedding-001")
vector_db = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory="./chroma_db"
)

# Configure the database to act as a document retriever
retriever = vector_db.as_retriever(search_kwargs={"k": 2})

# Define the hidden prompt structure for the LLM
template = """
Use the following pieces of retrieved context to answer the question. 
If you don't know the answer, just say that you don't know. 
Use three sentences maximum and keep the answer concise.

Context: {context}

Question: {question}

Answer:
"""
prompt = PromptTemplate.from_template(template)

# Initialize the free Gemini model tier
llm = ChatGoogleGenerativeAI(model="gemini-3.5-flash", temperature=0)

# Helper function to stitch retrieved chunks into a single text block
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)


# Connect everything together using LangChain Expression Language (LCEL)
rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
)
"""
user_question = "What days can I work from home?"
print(f"\nQuestion: {user_question}")

response = rag_chain.invoke(user_question)
# print(f"Answer: {response.content}")

# Clean up the output if Gemini returns a list of content blocks
if isinstance(response.content, list):
    clean_answer = response.content[0]['text']
else:
    clean_answer = response.content

print(f"Answer: {clean_answer}")
"""

# Chat with your PDF in a continuous loop
print("\n--- PDF Chatbot Initialized ---")
print("Type 'exit' or 'quit' to stop.")

while True:
    # 1. Wait for the user to type a question
    user_question = input("\nYour Question: ")

    # 2. Allow the user to break the loop and close the program
    if user_question.lower() in ['exit', 'quit']:
        print("Shutting down chatbot. Goodbye!")
        break

    # 3. Send the question through our RAG chain
    response = rag_chain.invoke(user_question)

    # 4. Clean up the output format
    if isinstance(response.content, list):
        clean_answer = response.content[0]['text']
    else:
        clean_answer = response.content

    # 5. Print the final answer to the console
    print(f"Answer: {clean_answer}")
</code></pre>
<h4 id="heading-taking-it-out-of-the-terminal">Taking it out of the terminal</h4>
<p>Once you have your terminal chatbot working, you probably want to give it a proper visual interface. The easiest way to do this in Python is using an open-source library called <strong>Gradio</strong>. <a href="https://blog.ashutoshkrris.in/build-ai-apps-with-gradio-turn-your-python-scripts-into-web-apps">Gradio</a> has a built-in <code>ChatInterface</code> feature that can wrap your existing RAG code and automatically generate a beautiful, ChatGPT-style web UI in your browser with just three extra lines of code. It's highly recommended as your next mini-project.</p>
<h2 id="heading-the-full-data-flow">The Full Data Flow</h2>
<p>To truly solidify your understanding, let's map out the exact lifecycle of a single user question in our system:</p>
<img src="https://cdn.hashnode.com/uploads/covers/61c1acb4a90dea775da8262b/af98284c-39c6-4cc7-bb4f-13955b659048.png" alt="af98284c-39c6-4cc7-bb4f-13955b659048" style="display:block;margin:0 auto" width="1577" height="621" loading="lazy">

<h3 id="heading-breaking-down-the-execution-timeline">Breaking Down the Execution Timeline</h3>
<ol>
<li><p><strong>The request begins:</strong> The user interfaces with our console and asks a text-based question: "How much vacation do I get?" At this exact moment, our application code takes control of the program flow.</p>
</li>
<li><p><strong>The text-to-vector translation:</strong> Computers can't compute similarity using raw text characters. Our app makes a fast network call to the Google Embedding Model, handing over the raw question. The model converts the text into a massive array of numbers that mathematically represents the user's intent.</p>
</li>
<li><p><strong>The database distance calculation:</strong> Our application script takes those coordinate numbers and passes them directly to ChromaDB. ChromaDB scans the local hard drive, running a similarity math function against the numbers stored for each of our PDF chunks. It locates the text chunk mentioning "20 days of paid time off" because its coordinates are physically closest to the query coordinates.</p>
</li>
<li><p><strong>The prompt augmentation:</strong> ChromaDB hands the raw text strings of those relevant pieces back to our script. The code automatically unrolls our prompt template, plugging the raw chunks into the {context} slot and the user's original text into the {question} slot.</p>
</li>
<li><p><strong>The final generation:</strong> Our application drops this combined package into the final network call, pushing it directly to the Gemini LLM. Because temperature=0 is configured, the model acts strictly as a reading comprehension engine. It reads the custom context, formats a clean sentence, and sends it back to our terminal to be printed out beautifully for the user.</p>
</li>
</ol>
<h2 id="heading-common-rag-problems">Common RAG Problems</h2>
<p>Building a simple RAG app is easy. Building a RAG app that works perfectly in production is very difficult. Here are the most common problems engineers face and how they fix them.</p>
<h3 id="heading-1-bad-chunking">1. Bad Chunking</h3>
<p>If your chunks are too large, they include irrelevant information that confuses the LLM. If they're too small, they lose vital context. Engineers can solve this by experimenting with different chunk sizes or using semantic chunking (splitting by whole sentences or paragraphs rather than strict character counts).</p>
<h3 id="heading-2-irrelevant-retrieval">2. Irrelevant Retrieval</h3>
<p>Sometimes semantic search fails. If a user searches for "Apple" expecting information about fruit, but the database only has data about the tech company, the system will confidently return tech company documents. Engineers can fix this by adjusting the embedding models or adding keyword search rules.</p>
<h3 id="heading-3-hallucinations">3. Hallucinations</h3>
<p>Even with RAG, an LLM might ignore the retrieved context and rely on its training memory. Engineers mitigate this by heavily engineering the prompt template with strict rules like "ONLY use the provided text."</p>
<h3 id="heading-4-latency">4. Latency</h3>
<p>RAG requires an embedding network call, a database search, and an LLM network call. This takes time. Engineers can optimize this by using faster, locally hosted embedding models or caching common questions.</p>
<h3 id="heading-5-stale-data">5. Stale Data</h3>
<p>If HR updates the company policy PDF, the vector database still holds the old numbers. The AI will give outdated answers. Engineers build update pipelines that automatically delete old vectors and embed new ones whenever a source file changes.</p>
<h2 id="heading-advanced-rag-concepts">Advanced RAG Concepts</h2>
<p>Once you master basic RAG, the AI engineering world opens up to highly advanced techniques.</p>
<h3 id="heading-hybrid-search">Hybrid Search</h3>
<p>Vector databases are great at understanding meaning, but bad at finding exact ID numbers or specific names. Hybrid search combines traditional keyword search (like searching a SQL database) with semantic vector search to get the best of both worlds.</p>
<h3 id="heading-reranking">Reranking</h3>
<p>Sometimes the vector database returns 10 chunks, but the best answer is accidentally placed at the bottom of the list. Reranking uses a second, specialized AI model to read the retrieved chunks and sort them strictly by relevance before sending them to the LLM.</p>
<h3 id="heading-agentic-rag">Agentic RAG</h3>
<p>Instead of forcing the system to retrieve documents every single time, Agentic RAG uses an AI "Agent" to decide if it even needs to search. If you say "Hello", the agent skips the database and just says "Hi". If you ask a hard question, it decides to query the database.</p>
<h3 id="heading-graph-rag">Graph RAG</h3>
<p>Instead of breaking text into isolated chunks, Graph RAG extracts entities (people, places, concepts) and maps how they relate to each other in a Knowledge Graph. This is incredibly powerful for complex datasets with deep relationships.</p>
<h3 id="heading-multi-modal-rag">Multi-Modal RAG</h3>
<p>Traditional RAG only reads text. Multi-modal RAG processes images, charts, and audio files, allowing users to ask questions like, "What does the graph on page 4 indicate?"</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Retrieval-Augmented Generation is the bridge between incredible reasoning engines (LLMs) and reliable factual knowledge (your data).</p>
<p>Understanding RAG is no longer optional for software engineers. Nearly every enterprise software product being built today involves some form of it. By learning how chunking, embeddings, vector databases, and prompt augmentation work together, you have demystified the magic behind modern AI.</p>
<p>Your next step is to build on the code we wrote today. Try pointing the PDF loader to your résumé, a school textbook, or a financial report. Once you experience your own code answering questions about your personal data, you'll start to truly understand the power of AI engineering.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ From Symptoms to Root Cause: How to Use the 5 Whys Technique ]]>
                </title>
                <description>
                    <![CDATA[ Most teams don't struggle because they can't fix problems. They struggle because they fix the wrong thing. An API fails in production. You restart the service, errors go away, and it feels resolved. U ]]>
                </description>
                <link>https://www.freecodecamp.org/news/from-symptoms-to-root-cause-how-to-use-the-5-whys-technique/</link>
                <guid isPermaLink="false">69ea4d69904b915438990f19</guid>
                
                    <category>
                        <![CDATA[ problem solving skills ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ debugging ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Thu, 23 Apr 2026 16:48:41 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/b5dbd964-9a03-448d-92a5-92e3b4a47fef.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Most teams don't struggle because they can't fix problems. They struggle because they fix the wrong thing.</p>
<p>An API fails in production. You restart the service, errors go away, and it feels resolved. Until it happens again. And again. What's happening here is simple: you're treating symptoms, not the underlying cause.</p>
<p>The <strong>5 Whys technique</strong> is a straightforward way to deal with this. It comes from the Toyota Production System and was designed to help teams dig deeper into problems instead of settling for quick fixes.</p>
<p>The idea is simple. Ask "why" repeatedly until you reach the real cause.</p>
<p>But in practice, this is where things go wrong.</p>
<p>Teams often:</p>
<ul>
<li><p>Stop too early</p>
</li>
<li><p>Assume answers without checking data</p>
</li>
<li><p>Focus on people instead of systems</p>
</li>
<li><p>Treat "five" as a rule instead of a guideline</p>
</li>
</ul>
<p>So even though the process looks structured, the outcome is still shallow.</p>
<p>In this article, we'll focus on how to actually use the 5 Whys in real situations. Not just the theory, but what it looks like when you apply it to an engineering problem.</p>
<h3 id="heading-heres-what-well-cover">Here's What We'll Cover:</h3>
<ul>
<li><p><a href="#heading-what-is-the-5-whys-technique">What is the 5 Whys Technique?</a></p>
</li>
<li><p><a href="#heading-origins-of-the-5-whys-method">Origins of the 5 Whys Method</a></p>
</li>
<li><p><a href="#heading-how-to-conduct-an-effective-5-whys-analysis">How to Conduct an Effective 5 Whys Analysis</a></p>
</li>
<li><p><a href="#heading-real-world-example-applying-5-whys-in-an-engineering-scenario">Real-World Example: Applying 5 Whys in an Engineering Scenario</a></p>
</li>
<li><p><a href="#heading-when-to-use-and-when-not-to-use-5-whys">When to Use (and When Not to Use) 5 Whys</a></p>
</li>
<li><p><a href="#heading-benefits-of-the-5-whys-technique">Benefits of the 5 Whys Technique</a></p>
</li>
<li><p><a href="#heading-common-pitfalls-and-limitations">Common Pitfalls and Limitations</a></p>
</li>
<li><p><a href="#heading-tips-for-using-5-whys-effectively">Tips for Using 5 Whys Effectively</a></p>
</li>
<li><p><a href="#heading-summary">Summary</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-5-whys-technique">What is the 5 Whys Technique?</h2>
<p>The 5 Whys technique is a way to break down a problem by repeatedly asking why it happened, with the goal of reaching a cause that actually explains the issue and can be addressed.</p>
<p>At its core, it's not about the number five. The name can be misleading. What matters is the process of following a chain of cause and effect until the explanation stops being superficial and starts becoming useful.</p>
<p>Each answer you uncover should move you one level deeper. You start with what went wrong, then explore what led to it, and continue until you reach something that is both believable and actionable. In most real situations, that final answer is not a single event but a gap in a system, a missing check, or an assumption that was never validated.</p>
<p>The technique became widely known through the Toyota Production System, where it was used to improve processes by focusing on causes rather than quick fixes.</p>
<p>That context is important because it highlights the original intent. The goal was not just to explain problems, but to prevent them from happening again.</p>
<p>A simple example makes this clearer. Imagine a mobile app suddenly starts crashing after a release. Asking "Why?" might look like this:</p>
<ol>
<li><p>Why is the app crashing? → Because a null value is being accessed in the code.</p>
</li>
<li><p>Why is there a null value? → Because the API response is missing a required field</p>
</li>
<li><p>Why is the field missing? → Because a recent backend change made the field optional.</p>
</li>
<li><p>Why was this change not handled in the app? → Because the app assumes the field is always present.</p>
</li>
<li><p>Why was this assumption not caught earlier? → Because there are no contract tests validating API responses.</p>
</li>
</ol>
<p>At this point, the issue is no longer just "fix the null check". The deeper problem is the lack of validation between systems, which allows breaking changes to slip through.</p>
<p>A useful way to think about the 5 Whys is that it forces you to stay with the problem a little longer than you normally would. Most of the time, the first explanation feels sufficient, so it's easy to stop there. This method pushes you to go one step further, and then another, until the explanation holds up under scrutiny.</p>
<p>At the same time, it's not a rigid formula. You might reach a solid root cause in three steps, or it might take more than five. The quality of the reasoning matters more than the count.</p>
<h2 id="heading-origins-of-the-5-whys-method">Origins of the 5 Whys Method</h2>
<p>The 5 Whys method comes from the Toyota Production System, a manufacturing approach focused on continuous improvement and problem solving at the source.</p>
<p>It's often associated with Sakichi Toyoda, whose philosophy was simple: don’t just fix a problem. Understand why it happened so it doesn't happen again.</p>
<p>Inside Toyota, this wasn't treated as a formal tool or checklist. It was part of the day-to-day way of working. When something went wrong on the production line, the goal wasn't to get things running quickly and move on. The goal was to stop, investigate, and make sure the same issue wouldn't repeat.</p>
<p>That mindset is important to understand. The 5 Whys was never meant to be a rigid exercise where you ask five questions and stop. It was a way to encourage deeper thinking and accountability in processes.</p>
<p>Another key idea in the Toyota system is that problems are usually caused by processes, not people. Instead of asking "who made the mistake", the focus is on "what allowed this mistake to happen". The 5 Whys fits naturally into this approach because it pushes you toward system level causes rather than individual blame.</p>
<p>Over time, the method spread beyond manufacturing and is now used in software engineering, product teams, operations, and many other fields. The context has changed, but the core idea remains the same: if you don't understand the cause, you're likely to see the same problem again.</p>
<p>This origin story is useful not just as background, but as a reminder of intent. The value of the 5 Whys doesn't come from the questions themselves. It comes from the discipline of not settling for the first answer.</p>
<h2 id="heading-how-to-conduct-an-effective-5-whys-analysis">How to Conduct an Effective 5 Whys Analysis</h2>
<p>A 5 Whys analysis works best when it is treated as a structured way of thinking, not a checklist to rush through. The quality of the outcome depends less on how many times you ask "why" and more on how carefully you reason through each step.</p>
<p>It helps to approach it in stages, each with a clear purpose.</p>
<h3 id="heading-step-1-define-the-problem-clearly">Step 1: Define the Problem Clearly</h3>
<p>Start with a problem statement that is specific and observable. Avoid vague descriptions like "the system is slow" or "things are failing". Instead, describe what actually happened in a way that can be verified.</p>
<p>For example, "API response time exceeded 5 seconds for 30 percent of requests between 2 PM and 3 PM" is much more useful than "API is slow".</p>
<p>A clear problem statement keeps the analysis grounded. If the starting point is fuzzy, the entire chain of reasoning will drift.</p>
<h3 id="heading-step-2-ask-why-iteratively">Step 2: Ask "Why" Iteratively</h3>
<p>Once the problem is defined, begin asking why it happened. Each answer should directly address the question before it and naturally lead to the next one.</p>
<p>The key here is continuity. Every step should feel like a logical extension of the previous one. If you find yourself jumping topics or introducing unrelated explanations, it's a sign that the chain is breaking.</p>
<p>Keep going until the answers stop being immediate symptoms and start pointing toward underlying conditions or decisions.</p>
<p>Also, don't force the process to stop at five. Some problems may need fewer steps, while others may need more. What matters is reaching a point where the explanation is meaningful and actionable.</p>
<h3 id="heading-step-3-validate-each-answer-with-evidence">Step 3: Validate Each Answer with Evidence</h3>
<p>This is where many analyses go wrong. It's easy to come up with plausible answers, but plausibility is not enough.</p>
<p>Each "why" should be backed by some form of evidence. This could be logs, metrics, recent changes, or direct observation. If an answer can't be verified, treat it as a hypothesis and confirm it before moving forward.</p>
<p>Without validation, the entire analysis becomes a chain of assumptions. Even if the final answer sounds reasonable, it may not reflect reality.</p>
<h3 id="heading-step-4-identify-the-root-cause">Step 4: Identify the Root Cause</h3>
<p>A good root cause is one that explains the sequence of events and can be acted upon to prevent the issue in the future.</p>
<p>In many cases, this turns out to be a gap in a process rather than a single technical failure. It could be a missing validation step, an incomplete test, or an assumption that was never challenged.</p>
<p>If the final answer still feels like a symptom, you probably need to go one level deeper. On the other hand, if the answer points to something you can change in your system or workflow, you are likely in the right place.</p>
<h3 id="heading-step-5-define-corrective-actions">Step 5: Define Corrective Actions</h3>
<p>The analysis is only useful if it leads to meaningful action.</p>
<p>Once you've identified the root cause, the next step is to define changes that prevent the problem from happening again. These should go beyond quick fixes and address the underlying issue.</p>
<p>For example, instead of just fixing a bug, you might introduce better testing, add monitoring, or improve review processes.</p>
<p>Good corrective actions share a few traits: they're specific, practical to implement, and they directly address the root cause identified in the analysis.</p>
<h2 id="heading-real-world-example-applying-5-whys-in-an-engineering-scenario">Real-World Example: Applying 5 Whys in an Engineering Scenario</h2>
<p>To see how this works in practice, let’s walk through a realistic backend issue. The goal here is not just to reach an answer, but to show how each step builds on evidence and leads to something actionable.</p>
<h3 id="heading-the-problem">The Problem:</h3>
<p>Users report intermittent failures while fetching order details:</p>
<pre><code class="language-bash">GET /api/orders/{id}
→ HTTP 500 Internal Server Error
</code></pre>
<p>Application logs show:</p>
<pre><code class="language-plaintext">// Java 21 example (Spring Boot style logging)
logger.error("Database connection timeout while fetching order", ex);
</code></pre>
<p>At this point, it's tempting to conclude that the database is the problem. But that's only what we can see on the surface.</p>
<h3 id="heading-applying-the-5-whys">Applying the 5 Whys</h3>
<h4 id="heading-1-why-did-the-api-return-a-500-error">1. Why did the API return a 500 error?</h4>
<p>Because the database query timed out.</p>
<p>This is directly supported by the error logs, so we can treat it as a confirmed fact.</p>
<h4 id="heading-2-why-did-the-query-time-out">2. Why did the query time out?</h4>
<p>Because the database connection pool was exhausted.</p>
<p>Metrics show that all available connections were in use during peak traffic.</p>
<h4 id="heading-3-why-was-the-connection-pool-exhausted">3. Why was the connection pool exhausted?</h4>
<p>Because some requests were holding database connections for too long.</p>
<p>Slow query logs confirm that a subset of queries had unusually high execution times.</p>
<h4 id="heading-4-why-were-some-queries-slow">4. Why were some queries slow?</h4>
<p>Because a recently introduced feature added a query on a non-indexed column.</p>
<p>Looking at recent deployments reveals a change that introduced filtering without proper indexing.</p>
<h4 id="heading-5-why-was-an-unoptimized-query-deployed-to-production">5. Why was an unoptimized query deployed to production?</h4>
<p>Because there is no performance validation step in the development or release process.</p>
<p>There are no checks in code review or CI/CD to catch inefficient database queries before deployment.</p>
<h3 id="heading-root-cause">Root Cause</h3>
<p>The issue is not the timeout itself.</p>
<p>It's this:</p>
<blockquote>
<p>The system allows inefficient database queries to reach production without any safeguards.</p>
</blockquote>
<img src="https://cdn.hashnode.com/uploads/covers/61c1acb4a90dea775da8262b/f93fb121-d5ac-45bc-8b3b-cc4f915c48a3.png" alt="f93fb121-d5ac-45bc-8b3b-cc4f915c48a3" style="display:block;margin:0 auto" width="423" height="544" loading="lazy">

<h3 id="heading-what-a-shallow-fix-would-look-like">What a Shallow Fix Would Look Like</h3>
<p>If we stopped early, we might:</p>
<ul>
<li><p>Increase the database timeout</p>
</li>
<li><p>Increase the connection pool size</p>
</li>
</ul>
<p>These might reduce the frequency of failures, but they don't solve the underlying problem.</p>
<h3 id="heading-what-a-strong-fix-looks-like">What a Strong Fix Looks Like</h3>
<p>A proper 5 Whys analysis leads to changes that improve the system:</p>
<ul>
<li><p>Add appropriate indexing for frequently queried fields</p>
</li>
<li><p>Introduce query performance checks in CI/CD pipelines</p>
</li>
<li><p>Add monitoring and alerts for slow queries</p>
</li>
<li><p>Include database considerations in code reviews</p>
</li>
</ul>
<h3 id="heading-why-this-example-matters">Why This Example Matters</h3>
<p>The difference between a shallow fix and a real solution is depth.</p>
<p>The first explanation often feels sufficient, especially under pressure. But stopping there means the issue is likely to return in a different form.</p>
<p>The value of the 5 Whys comes from following the chain all the way to something you can change in your system.</p>
<h2 id="heading-when-to-use-and-when-not-to-use-5-whys">When to Use (and When Not to Use) 5 Whys</h2>
<p>Like any problem-solving method, the 5 Whys is useful in the right context and less effective in others. Knowing when to apply it is just as important as knowing how to use it.</p>
<p>If used appropriately, it can uncover meaningful insights. If used in the wrong situation, it can lead to oversimplified or misleading conclusions</p>
<h3 id="heading-when-to-use-5-whys">When to Use 5 Whys</h3>
<p>The 5 Whys is most useful when your goal is to understand <strong>why something happened</strong>, not just to fix it and move on.</p>
<p>It works well in situations where problems are recurring or not fully explained by the first answer. For example, production incidents, repeated bugs, or issues that reappear after a quick fix are strong signals that you need deeper analysis. In these cases, the technique helps uncover what is happening beneath the surface.</p>
<p>It's also effective during retrospectives and postmortems. When a release doesn't go as expected or a sprint runs into issues, the 5 Whys helps teams move beyond observations like "this failed" and get to "why did this fail in the first place".</p>
<p>In general, use it when:</p>
<ul>
<li><p>The problem is not obvious</p>
</li>
<li><p>The issue has occurred more than once</p>
</li>
<li><p>You want to prevent recurrence, not just resolve the current instance</p>
</li>
</ul>
<h3 id="heading-when-not-to-use-5-whys">When Not to Use 5 Whys</h3>
<p>The 5 Whys has its limits, and using it in the wrong context can lead to oversimplified conclusions.</p>
<p>If a problem involves multiple interacting factors, a single chain of "why" questions may not capture the full picture. Complex systems often have several contributing causes, and forcing them into one linear explanation can hide important details. In such cases, the 5 Whys should be combined with other approaches.</p>
<p>It's also less effective when there's not enough data. If each answer is based on assumptions rather than evidence, the analysis quickly becomes unreliable. The method depends on validation at every step.</p>
<p>Another limitation is in time-critical situations. During an active incident, the priority is to restore the system. The deeper analysis should happen later, once things are stable.</p>
<p>Finally, if your goal is quantitative analysis or optimization, the 5 Whys alone isn't enough. You'll need more data-driven methods to support decision making.</p>
<p>A simple rule of thumb is this. If you are trying to <strong>learn from a problem</strong>, use the 5 Whys. If you are trying to <strong>fix something immediately or analyze complex data</strong>, use it carefully or alongside other techniques.</p>
<h2 id="heading-benefits-of-the-5-whys-technique">Benefits of the 5 Whys Technique</h2>
<p>The 5 Whys technique is simple, but it offers several powerful benefits that can help you solve problems more effectively and make lasting improvements. Here are the key advantages:</p>
<h3 id="heading-simple-and-easy-to-apply">Simple and Easy to Apply</h3>
<p>One of the biggest strengths of the 5 Whys is how easy it is to start using. You don't need special tools, training, or complex frameworks. It can be applied in a quick discussion, during debugging, or as part of a formal postmortem.</p>
<p>This low barrier makes it accessible across teams, regardless of experience level.</p>
<h3 id="heading-encourages-deeper-thinking">Encourages Deeper Thinking</h3>
<p>The method naturally pushes you to go beyond the first explanation. Instead of reacting to what's visible, it encourages you to question why the problem occurred in the first place.</p>
<p>This shift from surface-level fixes to deeper understanding often leads to better decisions.</p>
<h3 id="heading-promotes-system-level-improvements">Promotes System-Level Improvements</h3>
<p>When used correctly, the focus moves away from individual people and toward systems. Instead of asking who made a mistake, the analysis asks what allowed the mistake to happen.</p>
<p>This leads to improvements in processes, safeguards, and overall system design rather than one-off fixes.</p>
<h3 id="heading-works-well-in-team-settings">Works Well in Team Settings</h3>
<p>Because the approach is simple, it's easy for multiple people to contribute. Different perspectives help uncover gaps that might otherwise be missed.</p>
<p>It also creates a shared understanding of the problem, which is valuable during retrospectives and incident reviews.</p>
<h3 id="heading-helps-prevent-recurring-issues">Helps Prevent Recurring Issues</h3>
<p>Quick fixes often solve the immediate problem but don't stop it from happening again. The 5 Whys helps identify underlying causes, which makes it easier to prevent similar issues in the future.</p>
<p>Over time, this leads to more stable systems and fewer repeated incidents.</p>
<h2 id="heading-common-pitfalls-and-limitations">Common Pitfalls and Limitations</h2>
<p>While the 5 Whys technique is useful, it’s not always perfect. There are some limitations to keep in mind, so you can use it effectively and know when it might not be enough.</p>
<h3 id="heading-stopping-too-early">Stopping Too Early</h3>
<p>One of the most common mistakes is ending the analysis after the first or second answer. These early answers usually describe symptoms, not causes.</p>
<p>Stopping too soon leads to fixes that address the surface but leave the underlying issue unresolved.</p>
<h3 id="heading-treating-assumptions-as-facts">Treating Assumptions as Facts</h3>
<p>It's easy to come up with explanations that sound reasonable. But without evidence, they're just assumptions.</p>
<p>If each step isn't validated with logs, metrics, or observations, the entire analysis can drift away from reality.</p>
<h3 id="heading-focusing-on-individuals-instead-of-systems">Focusing on Individuals Instead of Systems</h3>
<p>Answers like "someone made a mistake" don't add much value. While they may be true, they don't explain why the system allowed that mistake to have an impact.</p>
<p>Focusing on processes and safeguards leads to more meaningful improvements.</p>
<h3 id="heading-oversimplifying-complex-problems">Oversimplifying Complex Problems</h3>
<p>The 5 Whys follows a linear chain of reasoning, but real-world systems often have multiple contributing factors.</p>
<p>Relying on a single chain can hide important interactions. In such cases, the method should be combined with other approaches.</p>
<h3 id="heading-treating-it-as-a-rigid-formula">Treating It as a Rigid Formula</h3>
<p>The name suggests asking "why" five times, but this shouldn't be taken literally. Some problems require fewer steps, while others need more.</p>
<p>Forcing the structure can lead to artificial or weak conclusions.</p>
<h3 id="heading-not-a-replacement-for-deeper-analysis">Not a Replacement for Deeper Analysis</h3>
<p>The 5 Whys isn't designed for every type of problem. For complex system failures, performance optimization, or data-heavy investigations, additional tools and methods are often required.</p>
<p>It works best as a starting point or a complement to other techniques, not a complete solution on its own.</p>
<h2 id="heading-tips-for-using-5-whys-effectively">Tips for Using 5 Whys Effectively</h2>
<p>To get the most out of the 5 Whys technique, there are a few tips that can help you use it effectively. These will guide you to ask the right questions and reach useful, actionable insights.</p>
<h3 id="heading-start-with-a-clear-specific-problem">Start with a Clear, Specific Problem</h3>
<p>A vague problem leads to vague answers. Spend a little extra time making sure the problem statement is precise and based on observable facts. This keeps the analysis grounded and avoids unnecessary detours.</p>
<h3 id="heading-base-every-step-on-evidence">Base Every Step on Evidence</h3>
<p>Treat each answer as something that needs to be verified. Use logs, metrics, recent changes, or direct observations to support your reasoning. If something can't be validated, call it out as a hypothesis and confirm it before moving forward.</p>
<h3 id="heading-keep-the-chain-logical-and-connected">Keep the Chain Logical and Connected</h3>
<p>Each "why" should naturally follow from the previous answer. If the reasoning starts to jump between unrelated ideas, pause and re-evaluate. A clean, logical chain is a strong indicator that you're on the right track.</p>
<h3 id="heading-focus-on-systems-not-individuals">Focus on Systems, Not Individuals</h3>
<p>Avoid stopping at explanations that point to human error. Instead, ask what allowed that error to have an impact. This shift in thinking leads to improvements that actually reduce the chances of similar issues in the future.</p>
<h3 id="heading-do-not-force-exactly-five-steps">Do Not Force Exactly Five Steps</h3>
<p>The number five is a guideline, not a rule. Some problems become clear in three steps, while others need more exploration. Stop when you reach a cause that's both convincing and actionable.</p>
<h3 id="heading-involve-the-right-people">Involve the Right People</h3>
<p>If possible, do the analysis as a group. People from different parts of the system bring different perspectives, which helps uncover details that might otherwise be missed. It also creates shared ownership of both the problem and the solution.</p>
<h3 id="heading-turn-insights-into-actions">Turn Insights into Actions</h3>
<p>The analysis only matters if it leads to change. Make sure the final outcome includes clear, practical steps that address the root cause. Without this, even a well-done analysis has limited impact.</p>
<h2 id="heading-summary">Summary</h2>
<p>The 5 Whys is a simple technique, but using it well takes some discipline.</p>
<p>At its core, it's about resisting the urge to stop at the first explanation. By following the chain of cause and effect, you move from symptoms to something you can actually fix. In many cases, that turns out to be a gap in a process rather than a one-off failure.</p>
<p>When applied thoughtfully, it helps teams learn from problems instead of just reacting to them. Over time, this leads to better systems, fewer recurring issues, and more confidence in how problems are handled.</p>
<p>The key is to treat it as a way of thinking, not just a set of steps.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Build a Website Screenshot Generator with Python and Flask ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever needed to take screenshots of websites automatically – maybe to track visual changes, include them in reports, or generate previews? Doing this manually can be time-consuming, especially if you need to capture multiple pages regularly. ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-website-screenshot-generator-with-python-and-flask/</link>
                <guid isPermaLink="false">690245c804c1e198e965b3ef</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flask Framework ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Wed, 29 Oct 2025 16:50:16 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761756575904/17a940c5-352e-47a2-992e-a1973c030c05.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever needed to take screenshots of websites automatically – maybe to track visual changes, include them in reports, or generate previews? Doing this manually can be time-consuming, especially if you need to capture multiple pages regularly.</p>
<p>In this tutorial, you’ll learn how to build a simple website screenshot generator using Python and Flask. The app will let users enter any website URL and instantly get a screenshot of that page – all powered by a screenshot API.</p>
<p>You’ll use <a target="_blank" href="https://blog.ashutoshkrris.in/getting-started-with-flask"><strong>Flask</strong></a>, a lightweight web framework, to create a simple web interface and handle requests. Then, you’ll integrate an external API to capture website screenshots programmatically.</p>
<p>By the end of this tutorial, you’ll have learned how to:</p>
<ul>
<li><p>Build a basic Flask web app</p>
</li>
<li><p>Accept user input through an HTML form</p>
</li>
<li><p>Make HTTP requests to an external API</p>
</li>
<li><p>Display images dynamically on a web page</p>
</li>
</ul>
<p>This project is a great way to learn how APIs can extend the capabilities of your web applications, and how Python can easily handle tasks like image generation and display.</p>
<h3 id="heading-what-well-cover">What we’ll cover:</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-setting-up-the-project">Setting Up the Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-integrating-the-screenshotbase-api">Integrating the ScreenshotBase API</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-adding-customization-options">Adding Customization Options</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we start building the app, make sure you have a few basics covered:</p>
<h3 id="heading-1-python-installed">1. Python Installed</h3>
<p>You’ll need Python 3.9 or higher installed on your machine. You can check your version by running:</p>
<pre><code class="lang-bash">python --version
</code></pre>
<p>If you don’t have it, you can download it from <a target="_blank" href="https://www.python.org/downloads/">python.org/downloads</a>.</p>
<h3 id="heading-2-basic-knowledge-of-python-and-flask">2. Basic Knowledge of Python and Flask</h3>
<p>You don’t need to be a Flask expert. Just have some familiarity with how to create routes and templates, and how to handle form data in Flask.</p>
<p>If you’re new to Flask, don’t worry – we’ll go step-by-step.</p>
<h3 id="heading-3-screenshot-api-key">3. Screenshot API Key</h3>
<p>We’ll be using the <a target="_blank" href="https://screenshotbase.com/"><strong>ScreenshotBase API</strong></a> to capture website screenshots. It provides a simple REST endpoint that takes a URL and returns a screenshot image.</p>
<p>You can get a free API key by signing up on their website. Once you have the key, keep it handy, as we’ll use it when we integrate the API in the later steps.</p>
<h3 id="heading-4-a-code-editor-and-terminal">4. A Code Editor and Terminal</h3>
<p>You can use any editor you prefer, like VS Code, PyCharm, or even a simple text editor. Make sure your terminal or command prompt can run Python commands and install packages using <code>pip</code>.</p>
<h2 id="heading-setting-up-the-project">Setting Up the Project</h2>
<p>Let’s start by setting up a new Flask project from scratch.</p>
<h3 id="heading-step-1-create-a-new-folder">Step 1: Create a New Folder</h3>
<p>Create a new folder for your project. You can name it <code>screenshot-generator</code>:</p>
<pre><code class="lang-bash">mkdir screenshot-generator
<span class="hljs-built_in">cd</span> screenshot-generator
</code></pre>
<h3 id="heading-step-2-create-a-virtual-environment">Step 2: Create a Virtual Environment</h3>
<p>A virtual environment helps keep your project dependencies isolated from other Python projects.</p>
<p>Run the following commands:</p>
<pre><code class="lang-bash">python -m venv venv
</code></pre>
<p>Then activate it:</p>
<ul>
<li><p>On macOS/Linux:</p>
<pre><code class="lang-bash">  <span class="hljs-built_in">source</span> venv/bin/activate
</code></pre>
</li>
<li><p>On Windows:</p>
<pre><code class="lang-bash">  venv\Scripts\activate
</code></pre>
</li>
</ul>
<p>Once activated, you should see <code>(venv)</code> at the beginning of your terminal prompt.</p>
<h3 id="heading-step-3-install-dependencies">Step 3: Install Dependencies</h3>
<p>We’ll need two packages for this project:</p>
<ul>
<li><p><strong>Flask</strong> for building the web app</p>
</li>
<li><p><strong>Requests</strong> for making API calls to ScreenshotBase</p>
</li>
</ul>
<p>Install them using pip:</p>
<pre><code class="lang-bash">pip install flask requests
</code></pre>
<h3 id="heading-step-4-set-up-the-project-structure">Step 4: Set Up the Project Structure</h3>
<p>Inside your project folder, create the following files and folders:</p>
<pre><code class="lang-bash">screenshot-generator/
├── app.py
├── templates/
│   └── index.html
└── static/
</code></pre>
<p>Here’s what each part does:</p>
<ul>
<li><p><code>app.py</code>: main Python file that runs your Flask app</p>
</li>
<li><p><code>templates/</code>: stores HTML templates</p>
</li>
<li><p><code>static/</code>: stores images, CSS, and JavaScript files</p>
</li>
</ul>
<h3 id="heading-step-5-add-a-basic-flask-app">Step 5: Add a Basic Flask App</h3>
<p>Open <code>app.py</code> and add the following starter code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, render_template, request
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> os

app = Flask(__name__)
API_KEY = os.getenv(<span class="hljs-string">"SCREENSHOTBASE_API_KEY"</span>)
SCREENSHOTBASE_BASE_ENDPOINT = <span class="hljs-string">"https://api.screenshotbase.com/v1/take"</span>

<span class="hljs-meta">@app.route('/', methods=['GET', 'POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">home</span>():</span>
    <span class="hljs-keyword">if</span> request.method == <span class="hljs-string">'POST'</span>:
        url = request.form.get(<span class="hljs-string">'url'</span>)
        <span class="hljs-comment"># Placeholder: We’ll add the API call here in the next section</span>
        <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">'index.html'</span>, url=url)
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">'index.html'</span>)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    app.run(debug=<span class="hljs-literal">True</span>)
</code></pre>
<p>This sets up a minimal Flask application with one route (<code>/</code>). We’ll add the ScreenshotBase API call in the next section.</p>
<h3 id="heading-step-6-create-a-simple-html-template">Step 6: Create a Simple HTML Template</h3>
<p>Now that we’ve written the Flask backend to handle the screenshot request, let’s create the frontend for our app.</p>
<p>Inside your project folder, create a new directory named <code>templates</code>, and within it, add a file called <code>index.html</code>. Flask will automatically look for templates in this folder.</p>
<p>Here’s how the template should look:</p>
<pre><code class="lang-xml"><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> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</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">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Website Screenshot Generator<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">href</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css"</span>
      <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span>
      <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB"</span>
      <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>
    /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
        <span class="hljs-selector-tag">body</span> {
            <span class="hljs-attribute">padding-top</span>: <span class="hljs-number">2rem</span>;
        }
        <span class="hljs-selector-class">.screenshot-container</span> {
            <span class="hljs-attribute">max-height</span>: <span class="hljs-number">80vh</span>;
            <span class="hljs-attribute">overflow-y</span>: auto;
            <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#ddd</span>;
            <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
            <span class="hljs-attribute">background</span>: <span class="hljs-number">#f8f9fa</span>;
        }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container text-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-4"</span>&gt;</span>Website Screenshot Generator<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"POST"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"d-flex justify-content-center mb-4"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> 
                <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> 
                <span class="hljs-attr">name</span>=<span class="hljs-string">"url"</span> 
                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter website URL"</span> 
                <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control w-50 me-2"</span>
                <span class="hljs-attr">required</span>
            &gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-primary"</span>&gt;</span>Capture Screenshot<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

        {% if screenshot %}
            <span class="hljs-tag">&lt;<span class="hljs-name">h4</span>&gt;</span>Screenshot Preview:<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"screenshot-container mx-auto"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"{{ screenshot }}"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Website Screenshot"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"img-fluid rounded shadow"</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        {% elif request.method == 'POST' %}
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"alert alert-danger mt-3"</span>&gt;</span>
                Sorry, something went wrong while capturing the screenshot. Please try again.
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        {% endif %}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</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>This HTML template uses Bootstrap to keep the design clean and responsive without much custom styling. The form at the top allows users to enter any website URL and submit it to the Flask app using the <code>POST</code> method. Once the app retrieves the screenshot URL from the API, it dynamically renders the image on the page.</p>
<p>The <code>img-fluid</code> class from Bootstrap ensures the screenshot scales properly across all screen sizes while maintaining its aspect ratio. Also, the <code>.screenshot-container</code> provides a scrollable area, which helps display full-page screenshots without shrinking them too much or breaking the layout.</p>
<p>Now your project is set up and ready to capture real screenshots using the ScreenshotBase API.</p>
<p>In the next section, we’ll write the logic to call the ScreenshotBase API and display the resulting screenshot dynamically in the browser.</p>
<h2 id="heading-integrating-the-screenshotbase-api">Integrating the ScreenshotBase API</h2>
<p>Now that our Flask app is set up, let’s connect it to the ScreenshotBase API so we can generate real screenshots from any URL.</p>
<h3 id="heading-step-1-understanding-the-api-endpoint">Step 1: Understanding the API Endpoint</h3>
<p>The ScreenshotBase API provides a simple endpoint that takes a website URL and returns a screenshot image.</p>
<p>A typical API call looks like this:</p>
<pre><code class="lang-bash">GET https://api.screenshotbase.com/v1/take
</code></pre>
<p>You send the target website URL as a query parameter (<code>url</code>) and include your API key in the request header as <code>apikey</code>.</p>
<p>Here’s the cURL example from their documentation:</p>
<pre><code class="lang-bash">curl -G https://api.screenshotbase.com/v1/take?url=https%3A%2F%2Fbbc.com \
    -H <span class="hljs-string">"apikey: YOUR-API-KEY"</span>
</code></pre>
<p>This request captures a screenshot of <code>https://bbc.com</code> and returns the screenshot image as the response.</p>
<h3 id="heading-step-2-add-the-api-call-in-flask">Step 2: Add the API Call in Flask</h3>
<p>Let’s update our <code>home</code> route in <code>app.py</code> to send a request to the ScreenshotBase API when a user submits a URL.</p>
<p>Here’s the updated version of <code>app.py</code>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, render_template, request
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> os

app = Flask(__name__)
API_KEY = os.getenv(<span class="hljs-string">"SCREENSHOTBASE_API_KEY"</span>)
SCREENSHOTBASE_BASE_ENDPOINT = <span class="hljs-string">"https://api.screenshotbase.com/v1/take"</span>

<span class="hljs-meta">@app.route('/', methods=['GET', 'POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">home</span>():</span>
    screenshot_url = <span class="hljs-literal">None</span>

    <span class="hljs-keyword">if</span> request.method == <span class="hljs-string">'POST'</span>:
        target_url = request.form.get(<span class="hljs-string">'url'</span>)

        params = {<span class="hljs-string">"url"</span>: target_url}
        headers = {<span class="hljs-string">"apikey"</span>: API_KEY}

        <span class="hljs-keyword">try</span>:
            <span class="hljs-comment"># Send GET request to ScreenshotBase API</span>
            response = requests.get(SCREENSHOTBASE_BASE_ENDPOINT, params=params, headers=headers, timeout=<span class="hljs-number">30</span>)
            response.raise_for_status()

            <span class="hljs-comment"># Save the returned image</span>
            image_path = os.path.join(<span class="hljs-string">'static'</span>, <span class="hljs-string">'screenshot.png'</span>)
            <span class="hljs-keyword">with</span> open(image_path, <span class="hljs-string">'wb'</span>) <span class="hljs-keyword">as</span> f:
                f.write(response.content)

            screenshot_url = image_path

        <span class="hljs-keyword">except</span> requests.exceptions.RequestException <span class="hljs-keyword">as</span> e:
            print(<span class="hljs-string">f"Error capturing screenshot: <span class="hljs-subst">{e}</span>"</span>)

    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">'index.html'</span>, screenshot=screenshot_url)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    app.run(debug=<span class="hljs-literal">True</span>)
</code></pre>
<p>Here’s what this code does:</p>
<ol>
<li><p>Captures the user input (URL).</p>
</li>
<li><p>Sends a GET request to the <code>/v1/take</code> endpoint.</p>
</li>
<li><p>Passes your API key in the request header (<code>apikey</code>).</p>
</li>
<li><p>Saves the returned image to the <code>static</code> folder.</p>
</li>
<li><p>Displays the screenshot on the page.</p>
</li>
</ol>
<h3 id="heading-step-3-test-your-app">Step 3: Test Your App</h3>
<p>Run the app again:</p>
<pre><code class="lang-python">python app.py
</code></pre>
<p>Then open <a target="_blank" href="http://127.0.0.1:5000">http://127.0.0.1:5000</a> in your browser.</p>
<p>Enter a URL like:</p>
<pre><code class="lang-python">https://github.com/ashutoshkrris
</code></pre>
<p>After submitting, you should see the screenshot of that website displayed below the form.</p>
<p>Your Flask app is now fully functional! It takes a URL, sends it to the ScreenshotBase API, and displays the resulting screenshot.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761133080971/d0feb78c-09cb-4f01-b254-a56fca3a58c8.png" alt="d0feb78c-09cb-4f01-b254-a56fca3a58c8" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>In the next section, we’ll enhance the app by adding customization options like full-page screenshots, viewport settings, and delays. Later, we’ll explore the ScreenshotBase SDKs for popular languages like Python, Node.js, and PHP.</p>
<h2 id="heading-adding-customization-options">Adding Customization Options</h2>
<p>Right now, our app simply takes a screenshot of the given website URL using the default settings. However, most screenshot APIs (including the one we’re using) allow you to customize the output by passing additional parameters in the request.</p>
<p>The ScreenshotBase API offers several powerful customization options to help you tailor each screenshot to your needs:</p>
<ul>
<li><p><strong>Image format</strong>: Choose from <code>png</code>, <code>jpg</code>, <code>gif</code>, or <code>webp</code>, depending on your image quality or compression requirements.</p>
</li>
<li><p><strong>Full page capture</strong>: Capture the entire scrollable webpage (<code>full_page=1</code>) or just the visible viewport (<code>full_page=0</code>).</p>
</li>
<li><p><strong>Viewport dimensions</strong>: Set the browser window size with <code>viewport_width</code> and <code>viewport_height</code> to simulate screenshots from desktop, tablet, or mobile screens.</p>
</li>
</ul>
<p>These options make ScreenshotBase ideal for building automation tools, thumbnail generators, testing dashboards, and visual documentation systems. Let’s add these options to make our screenshot generator more flexible.</p>
<h3 id="heading-updating-the-flask-code">Updating the Flask Code</h3>
<p>Open your <code>app.py</code> file and update the route that handles form submissions to include these optional parameters:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, render_template, request
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> os

app = Flask(__name__)
API_KEY = os.getenv(<span class="hljs-string">"SCREENSHOTBASE_API_KEY"</span>, <span class="hljs-string">"scr_live_9Qkn1gs01rivZqrFk7lusXPiqAUg85J86aU6bHvG"</span>)
SCREENSHOTBASE_BASE_ENDPOINT = <span class="hljs-string">"https://api.screenshotbase.com/v1/take"</span>


<span class="hljs-meta">@app.route('/', methods=['GET', 'POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">home</span>():</span>
    screenshot_url = <span class="hljs-literal">None</span>

    <span class="hljs-keyword">if</span> request.method == <span class="hljs-string">'POST'</span>:
        target_url = request.form.get(<span class="hljs-string">'url'</span>)
        format_ = request.form.get(<span class="hljs-string">'format'</span>, <span class="hljs-string">'png'</span>)
        full_page = request.form.get(<span class="hljs-string">'full_page'</span>) == <span class="hljs-string">'on'</span>

        params = {
            <span class="hljs-string">"url"</span>: target_url,
            <span class="hljs-string">"format"</span>: format_,
            <span class="hljs-string">"full_page"</span>: int(full_page)
        }
        headers = {<span class="hljs-string">"apikey"</span>: API_KEY}

        <span class="hljs-keyword">try</span>:
            <span class="hljs-comment"># Send GET request to ScreenshotBase API</span>
            response = requests.get(SCREENSHOTBASE_BASE_ENDPOINT, params=params, headers=headers, timeout=<span class="hljs-number">30</span>)
            response.raise_for_status()

            <span class="hljs-comment"># Save the returned image</span>
            image_extension = format_ <span class="hljs-keyword">if</span> format_ != <span class="hljs-string">'jpeg'</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'jpg'</span>
            image_path = os.path.join(<span class="hljs-string">'static'</span>, <span class="hljs-string">f'screenshot.<span class="hljs-subst">{image_extension}</span>'</span>)
            <span class="hljs-keyword">with</span> open(image_path, <span class="hljs-string">'wb'</span>) <span class="hljs-keyword">as</span> f:
                f.write(response.content)

            screenshot_url = image_path

        <span class="hljs-keyword">except</span> requests.exceptions.RequestException <span class="hljs-keyword">as</span> e:
            print(<span class="hljs-string">f"Error capturing screenshot: <span class="hljs-subst">{e}</span>"</span>)

    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">'index.html'</span>, screenshot=screenshot_url)


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    app.run(debug=<span class="hljs-literal">True</span>)
</code></pre>
<h3 id="heading-updating-the-html-template">Updating the HTML Template</h3>
<p>Next, let’s modify the form in our <code>index.html</code> to include the customization options:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"POST"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"d-flex flex-column justify-content-center mb-4"</span>&gt;</span>
  <span class="hljs-comment">&lt;!-- URL and Submit Button in One Row --&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-group mb-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
      <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
      <span class="hljs-attr">name</span>=<span class="hljs-string">"url"</span>
      <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter website URL"</span>
      <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control w-50 me-2"</span>
      <span class="hljs-attr">required</span>
    /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-primary"</span>&gt;</span>Capture Screenshot<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

  <span class="hljs-comment">&lt;!-- Options in Two Columns --&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row g-3"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- Full Page Checkbox --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-6"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-check"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
          <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span>
          <span class="hljs-attr">name</span>=<span class="hljs-string">"full_page"</span>
          <span class="hljs-attr">id</span>=<span class="hljs-string">"full_page"</span>
          <span class="hljs-attr">class</span>=<span class="hljs-string">"form-check-input"</span>
        /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-check-label"</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"full_page"</span>&gt;</span>
          Capture Full Page Screenshot
        <span class="hljs-tag">&lt;/<span class="hljs-name">label</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">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Format Dropdown --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-6"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"format"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-label"</span>&gt;</span>Screenshot Format<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">select</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-select"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"format"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"format"</span> <span class="hljs-attr">required</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"png"</span>&gt;</span>PNG<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"jpg"</span>&gt;</span>JPG<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"gif"</span>&gt;</span>GIF<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"webp"</span>&gt;</span>WEBP<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Viewport Width --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-6"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"viewport_width"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-label"</span>&gt;</span>Viewport Width<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport_width"</span>
        <span class="hljs-attr">id</span>=<span class="hljs-string">"viewport_width"</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span>
        <span class="hljs-attr">value</span>=<span class="hljs-string">"1280"</span>
        <span class="hljs-attr">min</span>=<span class="hljs-string">"320"</span>
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- Viewport Height --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-6"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"viewport_height"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-label"</span>&gt;</span>Viewport Height<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport_height"</span>
        <span class="hljs-attr">id</span>=<span class="hljs-string">"viewport_height"</span>
        <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span>
        <span class="hljs-attr">value</span>=<span class="hljs-string">"720"</span>
        <span class="hljs-attr">min</span>=<span class="hljs-string">"320"</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">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<p>The updated form gives users control over how the screenshot is generated. The format dropdown lets them choose between PNG, JPG, GIF, or WEBP. The Full Page checkbox toggles whether the API captures the entire scrollable webpage or just the visible viewport. The viewport width and height fields define the browser window dimensions, which is useful if you want to simulate different device sizes or responsive layouts.</p>
<p>When the form is submitted, Flask reads these values and sends them as query parameters in the API request.</p>
<p>For example, a request to capture a full-page, 1920×1080 PNG screenshot of your site would look like this:</p>
<pre><code class="lang-xml">https://api.screenshotbase.com/v1/take?url=https%3A%2F%2Fgithub.com%2Fashutoshkrris&amp;format=png&amp;full_page=1&amp;viewport_width=1920&amp;viewport_height=1080
</code></pre>
<p>This flexibility makes it easy to fine-tune screenshots for different use cases – whether you’re generating thumbnails, testing responsiveness, or automating visual reports.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761134646387/b9145271-303f-408e-ab53-444ea125849e.gif" alt="b9145271-303f-408e-ab53-444ea125849e" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Note: If you prefer working with SDKs instead of direct API calls, ScreenshotBase also offers official SDKs for popular languages like Python, JavaScript, Ruby, PHP, and Go.</p>
<p>These SDKs provide a simpler and more convenient way to interact with the API, handling authentication and request formatting behind the scenes.</p>
<p>You can explore them in the <a target="_blank" href="https://screenshotbase.com/docs/sdks">ScreenshotBase SDK documentation</a>.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>In this tutorial, you learned how to build a simple Flask application that captures website screenshots using a third-party API. We explored how to send requests, handle image responses, and add customization options like image format, full-page capture, and viewport size – all while keeping the project lightweight and easy to extend.</p>
<p>This small project demonstrates how web automation tasks, such as generating previews or visual reports, can be simplified using modern APIs. You can build upon this foundation to create batch screenshot tools, visual monitoring systems, or even integrate screenshots into larger web applications.</p>
<p>The key takeaway is understanding how to interact with external APIs, process responses, and design a clean interface for users. These are skills that are essential for backend and full-stack developers alike.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Work with SQLite in Python – A Handbook for Beginners ]]>
                </title>
                <description>
                    <![CDATA[ SQLite is one of the most popular relational database management systems (RDBMS). It’s lightweight, meaning that it doesn’t take up much space on your system. One of its best features is that it’s serverless, so you don’t need to install or manage a ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/work-with-sqlite-in-python-handbook/</link>
                <guid isPermaLink="false">66fd1605986ae3c9e56b8ba4</guid>
                
                    <category>
                        <![CDATA[ SQLite ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Wed, 02 Oct 2024 09:44:37 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1727862097228/24433377-ebb8-49b5-b0ee-5736f629399d.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>SQLite is one of the most popular relational database management systems (RDBMS). It’s lightweight, meaning that it doesn’t take up much space on your system. One of its best features is that it’s serverless, so you don’t need to install or manage a separate server to use it.</p>
<p>Instead, it stores everything in a simple file on your computer. It also requires zero configuration, so there’s no complicated setup process, making it perfect for beginners and small projects.</p>
<p>SQLite is a great choice for small to medium applications because it’s easy to use, fast, and can handle most tasks that bigger databases can do, but without the hassle of managing extra software. Whether you're building a personal project or prototyping a new app, SQLite is a solid option to get things up and running quickly.</p>
<p>In this tutorial, you'll learn how to work with SQLite using Python. Here’s what we’re going to cover in this tutorial:</p>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-your-python-environment">How to Set Up Your Python Environment</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-an-sqlite-database">How to Create an SQLite Database</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-database-tables">How to Create Database Tables</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-insert-data-into-a-table">How to Insert Data into a Table</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-query-data">How to Query Data</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-update-and-delete-data">How to Update and Delete Data</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-transactions">How to Use Transactions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-optimize-sqlite-query-performance-with-indexing">How to Optimize SQLite Query Performance with Indexing</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-handle-errors-and-exceptions">How to Handle Errors and Exceptions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-export-and-import-data-bonus-section">How to Export and Import Data [Bonus Section]</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<p>This tutorial is perfect for anyone who wants to get started with databases without diving into complex setups.</p>
<h2 id="heading-how-to-set-up-your-python-environment">How to Set Up Your Python Environment</h2>
<p>Before working with SQLite, let’s ensure your Python environment is ready. Here’s how to set everything up.</p>
<h3 id="heading-installing-python">Installing Python</h3>
<p>If you don’t have Python installed on your system yet, you can download it from the official <a target="_blank" href="https://www.python.org/downloads/">Python website</a>. Follow the installation instructions for your operating system (Windows, macOS, or Linux).</p>
<p>To check if Python is installed, open your terminal (or command prompt) and type:</p>
<pre><code class="lang-bash">python --version
</code></pre>
<p>This should show the current version of Python installed. If it’s not installed, follow the instructions on the Python website.</p>
<h3 id="heading-installing-sqlite3-module">Installing SQLite3 Module</h3>
<p>The good news is that SQLite3 comes built-in with Python! You don’t need to install it separately because it’s included in the standard Python library. This means you can start using it right away without any additional setup.</p>
<h3 id="heading-how-to-create-a-virtual-environment-optional-but-recommended">How to Create a Virtual Environment (Optional but Recommended)</h3>
<p>It’s a good idea to create a virtual environment for each project to keep your dependencies organized. A virtual environment is like a clean slate where you can install packages without affecting your global Python installation.</p>
<p>To create a virtual environment, follow these steps:</p>
<ol>
<li><p>First, open your terminal or command prompt and navigate to the directory where you want to create your project.</p>
</li>
<li><p>Run the following command to create a virtual environment:</p>
</li>
</ol>
<pre><code class="lang-bash">python -m venv env
</code></pre>
<p>Here, <code>env</code> is the name of the virtual environment. You can name it anything you like.</p>
<ol start="3">
<li>Activate the virtual environment:</li>
</ol>
<pre><code class="lang-bash"><span class="hljs-comment"># Use the command for Windows</span>
env\Scripts\activate

<span class="hljs-comment"># Use the command for macOS/Linux:</span>
env/bin/activate
</code></pre>
<p>After activating the virtual environment, you’ll notice that your terminal prompt changes, showing the name of the virtual environment. This means you’re now working inside it.</p>
<h3 id="heading-installing-necessary-libraries">Installing Necessary Libraries</h3>
<p>We’ll need a few additional libraries for this project. Specifically, we’ll use:</p>
<ul>
<li><p><code>pandas</code>: This is an optional library for handling and displaying data in tabular format, useful for advanced use cases.</p>
</li>
<li><p><code>faker</code>: This library will help us generate fake data, like random names and addresses, which we can insert into our database for testing.</p>
</li>
</ul>
<p>To install <code>pandas</code> and <code>faker</code>, simply run the following commands:</p>
<pre><code class="lang-bash">pip install pandas faker
</code></pre>
<p>This installs both <code>pandas</code> and <code>faker</code> into your virtual environment. With this, your environment is set up, and you’re ready to start creating and managing your SQLite database in Python!</p>
<h2 id="heading-how-to-create-an-sqlite-database">How to Create an SQLite Database</h2>
<p>A database is a structured way to store and manage data so that it can be easily accessed, updated, and organized. It’s like a digital filing system that allows you to efficiently store large amounts of data, whether it’s for a simple app or a more complex system. Databases use tables to organize data, with rows and columns representing individual records and their attributes.</p>
<h3 id="heading-how-sqlite-databases-work">How SQLite Databases Work</h3>
<p>Unlike most other database systems, SQLite is a serverless database. This means that it doesn’t require setting up or managing a server, making it lightweight and easy to use. All the data is stored in a single file on your computer, which you can easily move, share, or back up. Despite its simplicity, SQLite is powerful enough to handle many common database tasks and is widely used in mobile apps, embedded systems, and small to medium-sized projects.</p>
<h3 id="heading-how-to-create-a-new-sqlite-database">How to Create a New SQLite Database</h3>
<p>Let’s create a new SQLite database and learn how to interact with it using Python’s <code>sqlite3</code> library.</p>
<h4 id="heading-connecting-to-the-database">Connecting to the Database</h4>
<p>Since <code>sqlite3</code> is pre-installed, you just need to import it in your Python script. To create a new database or connect to an existing one, we use the <code>sqlite3.connect()</code> method. This method takes the name of the database file as an argument. If the file doesn’t exist, SQLite will automatically create it.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3

<span class="hljs-comment"># Connect to the SQLite database (or create it if it doesn't exist)</span>
connection = sqlite3.connect(<span class="hljs-string">'my_database.db'</span>)
</code></pre>
<p>In this example, a file named <code>my_database.db</code> is created in the same directory as your script. If the file already exists, SQLite will just open the connection to it.</p>
<h4 id="heading-creating-a-cursor">Creating a Cursor</h4>
<p>Once you have a connection, the next step is to create a cursor object. The cursor is responsible for executing SQL commands and queries on the database.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create a cursor object</span>
cursor = connection.cursor()
</code></pre>
<h4 id="heading-closing-the-connection">Closing the Connection</h4>
<p>After you’ve finished working with the database, it’s important to close the connection to free up any resources. You can close the connection with the following command:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Close the database connection</span>
connection.close()
</code></pre>
<p>However, you should only close the connection once you’re done with all your operations.</p>
<p>When you run your Python script, a file named <code>my_database.db</code> will be created in your current working directory. You’ve now successfully created your first SQLite database!</p>
<h3 id="heading-how-to-use-context-manager-to-open-and-close-connections">How to Use Context Manager to Open and Close Connections</h3>
<p>Python provides a more efficient and cleaner way to handle database connections using the <code>with</code> statement, also known as a context manager. The <code>with</code> statement automatically opens and closes the connection, ensuring that the connection is properly closed even if an error occurs during the database operations. This eliminates the need to manually call <code>connection.close()</code>.</p>
<p>Here’s how you can use the <code>with</code> statement to handle database connections:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3

<span class="hljs-comment"># Step 1: Use 'with' to connect to the database (or create one) and automatically close it when done</span>
<span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:

    <span class="hljs-comment"># Step 2: Create a cursor object to interact with the database</span>
    cursor = connection.cursor()

    print(<span class="hljs-string">"Database created and connected successfully!"</span>)

<span class="hljs-comment"># No need to call connection.close(); it's done automatically!</span>
</code></pre>
<p>From now on, we’ll use the <code>with</code> statement in our upcoming code examples to manage database connections efficiently. This will make the code more concise and easier to maintain.</p>
<h2 id="heading-how-to-create-database-tables">How to Create Database Tables</h2>
<p>Now that we’ve created an SQLite database and connected to it, the next step is to create tables inside the database. A table is where we’ll store our data, organized in rows (records) and columns (attributes). For this example, we’ll create a table called <code>Students</code> to store information about students, which we’ll reuse in upcoming sections.</p>
<p>To create a table, we use SQL's <code>CREATE TABLE</code> statement. This command defines the table structure, including the column names and the data types for each column.</p>
<p>Here’s a simple SQL command to create a <code>Students</code> table with the following fields:</p>
<ul>
<li><p><code>id</code>: A unique identifier for each student (an integer).</p>
</li>
<li><p><strong>name</strong>: The student's name (text).</p>
</li>
<li><p><strong>age</strong>: The student's age (an integer).</p>
</li>
<li><p><strong>email</strong>: The student's email address (text).</p>
</li>
</ul>
<p>The SQL command to create this table would look like this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> Students (
    <span class="hljs-keyword">id</span> <span class="hljs-built_in">INTEGER</span> PRIMARY <span class="hljs-keyword">KEY</span> AUTOINCREMENT,
    <span class="hljs-keyword">name</span> <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    age <span class="hljs-built_in">INTEGER</span>,
    email <span class="hljs-built_in">TEXT</span>
);
</code></pre>
<p>We can execute this <code>CREATE TABLE</code> SQL command in Python using the <code>sqlite3</code> library. Let’s see how to do that.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3

<span class="hljs-comment"># Use 'with' to connect to the SQLite database and automatically close the connection when done</span>
<span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:

    <span class="hljs-comment"># Create a cursor object</span>
    cursor = connection.cursor()

    <span class="hljs-comment"># Write the SQL command to create the Students table</span>
    create_table_query = <span class="hljs-string">'''
    CREATE TABLE IF NOT EXISTS Students (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        age INTEGER,
        email TEXT
    );
    '''</span>

    <span class="hljs-comment"># Execute the SQL command</span>
    cursor.execute(create_table_query)

    <span class="hljs-comment"># Commit the changes</span>
    connection.commit()

    <span class="hljs-comment"># Print a confirmation message</span>
    print(<span class="hljs-string">"Table 'Students' created successfully!"</span>)
</code></pre>
<ul>
<li><p><code>IF NOT EXISTS</code>: This ensures that the table is only created if it doesn’t already exist, preventing errors if the table has been created before.</p>
</li>
<li><p><code>connection.commit()</code>: This saves (commits) the changes to the database.</p>
</li>
</ul>
<p>When you run the Python code above, it will create the <code>Students</code> table in the <code>my_database.db</code> database file. You’ll also see a message in the terminal confirming that the table has been created successfully.</p>
<p>If you’re using Visual Studio Code, you can install the <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=qwtel.sqlite-viewer">SQLite Viewer</a> extension to view SQLite databases.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1727514353100/522fc6f1-0363-41ca-a76a-b730470cb64a.png" alt="SQLite Viewer - VS Code Extension" class="image--center mx-auto" width="1240" height="532" loading="lazy"></p>
<h3 id="heading-data-types-in-sqlite-and-their-mapping-to-python">Data Types in SQLite and Their Mapping to Python</h3>
<p>SQLite supports several data types, which we need to understand when defining our tables. Here’s a quick overview of common SQLite data types and how they map to Python types:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>SQLite Data Type</td><td>Description</td><td>Python Equivalent</td></tr>
</thead>
<tbody>
<tr>
<td><strong>INTEGER</strong></td><td>Whole numbers</td><td><code>int</code></td></tr>
<tr>
<td><strong>TEXT</strong></td><td>Text strings</td><td><code>str</code></td></tr>
<tr>
<td><strong>REAL</strong></td><td>Floating-point numbers</td><td><code>float</code></td></tr>
<tr>
<td><strong>BLOB</strong></td><td>Binary data (e.g., images, files)</td><td><code>bytes</code></td></tr>
<tr>
<td><strong>NULL</strong></td><td>Represents no value or missing data</td><td><code>None</code></td></tr>
</tbody>
</table>
</div><p>In our <code>Students</code> table:</p>
<ul>
<li><p><code>id</code> is of type <code>INTEGER</code>, which maps to Python’s <code>int</code>.</p>
</li>
<li><p><code>name</code> and <code>email</code> are of type <code>TEXT</code>, which map to Python’s <code>str</code>.</p>
</li>
<li><p><code>age</code> is also of type <code>INTEGER</code>, mapping to Python’s <code>int</code>.</p>
</li>
</ul>
<h2 id="heading-how-to-insert-data-into-a-table">How to Insert Data into a Table</h2>
<p>Now that we have our <code>Students</code> table created, it’s time to start inserting data into the database. In this section, we’ll cover how to insert both single and multiple records using Python and SQLite, and how to avoid common security issues like SQL injection by using parameterized queries.</p>
<h3 id="heading-how-to-insert-a-single-record">How to Insert a Single Record</h3>
<p>To insert data into the database, we use the <code>INSERT INTO</code> SQL command. Let’s start by inserting a single record into our <code>Students</code> table.</p>
<p>Here’s the basic SQL syntax for inserting a single record:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> Students (<span class="hljs-keyword">name</span>, age, email) 
<span class="hljs-keyword">VALUES</span> (<span class="hljs-string">'John Doe'</span>, <span class="hljs-number">20</span>, <span class="hljs-string">'johndoe@example.com'</span>);
</code></pre>
<p>However, instead of writing SQL directly in our Python script with hardcoded values, we’ll use parameterized queries to make our code more secure and flexible. Parameterized queries help prevent SQL injection, a common attack where malicious users can manipulate the SQL query by passing harmful input.</p>
<p>Here’s how we can insert a single record into the <code>Students</code> table using a parameterized query:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3

<span class="hljs-comment"># Use 'with' to open and close the connection automatically</span>
<span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
    cursor = connection.cursor()

    <span class="hljs-comment"># Insert a record into the Students table</span>
    insert_query = <span class="hljs-string">'''
    INSERT INTO Students (name, age, email) 
    VALUES (?, ?, ?);
    '''</span>
    student_data = (<span class="hljs-string">'Jane Doe'</span>, <span class="hljs-number">23</span>, <span class="hljs-string">'jane@example.com'</span>)

    cursor.execute(insert_query, student_data)

    <span class="hljs-comment"># Commit the changes automatically</span>
    connection.commit()

    <span class="hljs-comment"># No need to call connection.close(); it's done automatically!</span>
    print(<span class="hljs-string">"Record inserted successfully!"</span>)
</code></pre>
<p>The <code>?</code> placeholders represent the values to be inserted into the table. The actual values are passed as a tuple (<code>student_data</code>) in the <code>cursor.execute()</code> method.</p>
<h3 id="heading-how-to-insert-multiple-records">How to Insert Multiple Records</h3>
<p>If you want to insert multiple records at once, you can use the <code>executemany()</code> method in Python. This method takes a list of tuples, where each tuple represents one record.</p>
<p>To make our example more dynamic, we can use the <code>Faker</code> library to generate random student data. This is useful for testing and simulating real-world scenarios.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> faker <span class="hljs-keyword">import</span> Faker
<span class="hljs-keyword">import</span> sqlite3

<span class="hljs-comment"># Initialize Faker</span>
fake = Faker([<span class="hljs-string">'en_IN'</span>])

<span class="hljs-comment"># Use 'with' to open and close the connection automatically</span>
<span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
    cursor = connection.cursor()

    <span class="hljs-comment"># Insert a record into the Students table</span>
    insert_query = <span class="hljs-string">'''
    INSERT INTO Students (name, age, email) 
    VALUES (?, ?, ?);
    '''</span>
    students_data = [(fake.name(), fake.random_int(
        min=<span class="hljs-number">18</span>, max=<span class="hljs-number">25</span>), fake.email()) <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(<span class="hljs-number">5</span>)]

    <span class="hljs-comment"># Execute the query for multiple records</span>
    cursor.executemany(insert_query, students_data)

    <span class="hljs-comment"># Commit the changes</span>
    connection.commit()

    <span class="hljs-comment"># Print confirmation message</span>
    print(<span class="hljs-string">"Fake student records inserted successfully!"</span>)
</code></pre>
<p>In this code:</p>
<ul>
<li><p><code>Faker()</code> generates random names, ages, and emails for students. Passing the locale(<code>[‘en_IN’]</code>) is optional.</p>
</li>
<li><p><code>cursor.executemany()</code>: This method allows us to insert multiple records at once, making the code more efficient.</p>
</li>
<li><p><code>students_data</code>: A list of tuples where each tuple represents one student’s data.</p>
</li>
</ul>
<h3 id="heading-how-to-handle-common-issues-sql-injection">How to Handle Common Issues: SQL Injection</h3>
<p>SQL injection is a security vulnerability where attackers can insert or manipulate SQL queries by providing harmful input. For example, an attacker might try to inject code like <code>'; DROP TABLE Students; --</code> to delete the table.</p>
<p>By using parameterized queries (as demonstrated above), we avoid this issue. The <code>?</code> placeholders in parameterized queries ensure that input values are treated as data, not as part of the SQL command. This makes it impossible for malicious code to be executed.</p>
<h2 id="heading-how-to-query-data">How to Query Data</h2>
<p>Now that we’ve inserted some data into our <code>Students</code> table, let’s learn how to retrieve the data from the table. We'll explore different methods for fetching data in Python, including <code>fetchone()</code>, <code>fetchall()</code>, and <code>fetchmany()</code>.</p>
<p>To query data from a table, we use the <code>SELECT</code> statement. Here’s a simple SQL command to select all columns from the <code>Students</code> table:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> Students;
</code></pre>
<p>This command retrieves all records and columns from the <code>Students</code> table. We can execute this <code>SELECT</code> query in Python and fetch the results.</p>
<h3 id="heading-how-to-fetch-all-records">How to Fetch All Records</h3>
<p>Here’s how we can fetch all records from the <code>Students</code> table:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3

<span class="hljs-comment"># Use 'with' to connect to the SQLite database</span>
<span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:

    <span class="hljs-comment"># Create a cursor object</span>
    cursor = connection.cursor()

    <span class="hljs-comment"># Write the SQL command to select all records from the Students table</span>
    select_query = <span class="hljs-string">"SELECT * FROM Students;"</span>

    <span class="hljs-comment"># Execute the SQL command</span>
    cursor.execute(select_query)

    <span class="hljs-comment"># Fetch all records</span>
    all_students = cursor.fetchall()

    <span class="hljs-comment"># Display results in the terminal</span>
    print(<span class="hljs-string">"All Students:"</span>)
    <span class="hljs-keyword">for</span> student <span class="hljs-keyword">in</span> all_students:
        print(student)
</code></pre>
<p>In this example, the <code>fetchall()</code> method retrieves all rows returned by the query as a list of tuples.</p>
<pre><code class="lang-bash">All Students:
(1, <span class="hljs-string">'Jane Doe'</span>, 23, <span class="hljs-string">'jane@example.com'</span>)
(2, <span class="hljs-string">'Bahadurjit Sabharwal'</span>, 18, <span class="hljs-string">'tristanupadhyay@example.net'</span>)
(3, <span class="hljs-string">'Zayyan Arya'</span>, 20, <span class="hljs-string">'yashawinibhakta@example.org'</span>)
(4, <span class="hljs-string">'Hemani Shukla'</span>, 18, <span class="hljs-string">'gaurikanarula@example.com'</span>)
(5, <span class="hljs-string">'Warda Kara'</span>, 20, <span class="hljs-string">'npatil@example.net'</span>)
(6, <span class="hljs-string">'Mitali Nazareth'</span>, 19, <span class="hljs-string">'sparekh@example.org'</span>)
</code></pre>
<h3 id="heading-how-to-fetch-a-single-record">How to Fetch a Single Record</h3>
<p>If you want to retrieve only one record, you can use the <code>fetchone()</code> method:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3

<span class="hljs-comment"># Use 'with' to connect to the SQLite database</span>
<span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:

    <span class="hljs-comment"># Create a cursor object</span>
    cursor = connection.cursor()

    <span class="hljs-comment"># Write the SQL command to select all records from the Students table</span>
    select_query = <span class="hljs-string">"SELECT * FROM Students;"</span>

    <span class="hljs-comment"># Execute the SQL command</span>
    cursor.execute(select_query)

    <span class="hljs-comment"># Fetch one record</span>
    student = cursor.fetchone()

    <span class="hljs-comment"># Display the result</span>
    print(<span class="hljs-string">"First Student:"</span>)
    print(student)
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">First Student:
(1, <span class="hljs-string">'Jane Doe'</span>, 23, <span class="hljs-string">'jane@example.com'</span>)
</code></pre>
<h3 id="heading-how-to-fetch-multiple-records">How to Fetch Multiple Records</h3>
<p>To fetch a specific number of records, you can use <code>fetchmany(size)</code>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3

<span class="hljs-comment"># Use 'with' to connect to the SQLite database</span>
<span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:

    <span class="hljs-comment"># Create a cursor object</span>
    cursor = connection.cursor()

    <span class="hljs-comment"># Write the SQL command to select all records from the Students table</span>
    select_query = <span class="hljs-string">"SELECT * FROM Students;"</span>

    <span class="hljs-comment"># Execute the SQL command</span>
    cursor.execute(select_query)

    <span class="hljs-comment"># Fetch three records</span>
    three_students = cursor.fetchmany(<span class="hljs-number">3</span>)

    <span class="hljs-comment"># Display results</span>
    print(<span class="hljs-string">"Three Students:"</span>)
    <span class="hljs-keyword">for</span> student <span class="hljs-keyword">in</span> three_students:
        print(student)
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">Three Students:
(1, <span class="hljs-string">'Jane Doe'</span>, 23, <span class="hljs-string">'jane@example.com'</span>)
(2, <span class="hljs-string">'Bahadurjit Sabharwal'</span>, 18, <span class="hljs-string">'tristanupadhyay@example.net'</span>)
(3, <span class="hljs-string">'Zayyan Arya'</span>, 20, <span class="hljs-string">'yashawinibhakta@example.org'</span>)
</code></pre>
<h3 id="heading-how-to-use-pandas-for-better-data-presentation">How to Use <code>pandas</code> for Better Data Presentation</h3>
<p>For better data presentation, we can use the <code>pandas</code> library to create a <code>DataFrame</code> from our query results. This makes it easier to manipulate and visualize the data.</p>
<p>Here’s how to fetch all records and display them as a pandas DataFrame:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3
<span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd

<span class="hljs-comment"># Use 'with' to connect to the SQLite database</span>
<span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
    <span class="hljs-comment"># Write the SQL command to select all records from the Students table</span>
    select_query = <span class="hljs-string">"SELECT * FROM Students;"</span>

    <span class="hljs-comment"># Use pandas to read SQL query directly into a DataFrame</span>
    df = pd.read_sql_query(select_query, connection)

<span class="hljs-comment"># Display the DataFrame</span>
print(<span class="hljs-string">"All Students as DataFrame:"</span>)
print(df)
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">All Students as DataFrame:
   id                  name  age                        email
0   1              Jane Doe   23             jane@example.com
1   2  Bahadurjit Sabharwal   18  tristanupadhyay@example.net
2   3           Zayyan Arya   20  yashawinibhakta@example.org
3   4         Hemani Shukla   18    gaurikanarula@example.com
4   5            Warda Kara   20           npatil@example.net
5   6       Mitali Nazareth   19          sparekh@example.org
</code></pre>
<p>The <code>pd.read_sql_query()</code> function executes the SQL query and directly returns the results as a pandas DataFrame.</p>
<h2 id="heading-how-to-update-and-delete-data">How to Update and Delete Data</h2>
<p>In this section, we’ll learn how to update existing records and delete records from our <code>Students</code> table using SQL commands in Python. This is essential for managing and maintaining your data effectively.</p>
<h3 id="heading-updating-existing-records">Updating Existing Records</h3>
<p>To modify existing records in a database, we use the SQL <code>UPDATE</code> command. This command allows us to change the values of specific columns in one or more rows based on a specified condition.</p>
<p>For example, if we want to update a student's age, the SQL command would look like this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">UPDATE</span> Students 
<span class="hljs-keyword">SET</span> age = <span class="hljs-number">21</span> 
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">name</span> = <span class="hljs-string">'Jane Doe'</span>;
</code></pre>
<p>Now, let’s write Python code to update a specific student's age in our <code>Students</code> table.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3

<span class="hljs-comment"># Use 'with' to connect to the SQLite database</span>
<span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
    cursor = connection.cursor()

    <span class="hljs-comment"># SQL command to update a student's age</span>
    update_query = <span class="hljs-string">'''
    UPDATE Students 
    SET age = ? 
    WHERE name = ?;
    '''</span>

    <span class="hljs-comment"># Data for the update</span>
    new_age = <span class="hljs-number">21</span>
    student_name = <span class="hljs-string">'Jane Doe'</span>

    <span class="hljs-comment"># Execute the SQL command with the data</span>
    cursor.execute(update_query, (new_age, student_name))

    <span class="hljs-comment"># Commit the changes to save the update</span>
    connection.commit()

    <span class="hljs-comment"># Print a confirmation message</span>
    print(<span class="hljs-string">f"Updated age for <span class="hljs-subst">{student_name}</span> to <span class="hljs-subst">{new_age}</span>."</span>)
</code></pre>
<p>In this example, we used parameterized queries to prevent SQL injection.</p>
<h3 id="heading-how-to-delete-records-from-the-table">How to Delete Records from the Table</h3>
<p>To remove records from a database, we use the SQL <code>DELETE</code> command. This command allows us to delete one or more rows based on a specified condition.</p>
<p>For example, if we want to delete a student named 'Jane Doe', the SQL command would look like this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> Students 
<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">name</span> = <span class="hljs-string">'Jane Doe'</span>;
</code></pre>
<p>Let’s write Python code to delete a specific student from our <code>Students</code> table using the <code>with</code> statement.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3

<span class="hljs-comment"># Use 'with' to connect to the SQLite database</span>
<span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
    cursor = connection.cursor()

    <span class="hljs-comment"># SQL command to delete a student</span>
    delete_query = <span class="hljs-string">'''
    DELETE FROM Students 
    WHERE name = ?;
    '''</span>

    <span class="hljs-comment"># Name of the student to be deleted</span>
    student_name = <span class="hljs-string">'Jane Doe'</span>

    <span class="hljs-comment"># Execute the SQL command with the data</span>
    cursor.execute(delete_query, (student_name,))

    <span class="hljs-comment"># Commit the changes to save the deletion</span>
    connection.commit()

    <span class="hljs-comment"># Print a confirmation message</span>
    print(<span class="hljs-string">f"Deleted student record for <span class="hljs-subst">{student_name}</span>."</span>)
</code></pre>
<h4 id="heading-important-considerations">Important Considerations</h4>
<ul>
<li><p><strong>Conditions</strong>: Always use the <code>WHERE</code> clause when updating or deleting records to avoid modifying or removing all rows in the table. Without a <code>WHERE</code> clause, the command affects every row in the table.</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1727519069500/f22be4cc-e75f-4492-af01-ed08f31361f3.jpeg" alt="357089 rows affected Meme" class="image--center mx-auto" width="939" height="939" loading="lazy"></p>
</li>
<li><p><strong>Backup</strong>: It’s good practice to back up your database before performing updates or deletions, especially in production environments.</p>
</li>
</ul>
<h2 id="heading-how-to-use-transactions">How to Use Transactions</h2>
<p>A transaction is a sequence of one or more SQL operations that are treated as a single unit of work. In the context of a database, a transaction allows you to perform multiple operations that either all succeed or none at all. This ensures that your database remains in a consistent state, even in the face of errors or unexpected issues.</p>
<p>For example, if you are transferring money between two bank accounts, you would want both the debit from one account and the credit to the other to succeed or fail together. If one operation fails, the other should not be executed to maintain consistency.</p>
<h3 id="heading-why-use-transactions">Why Use Transactions?</h3>
<ol>
<li><p><strong>Atomicity</strong>: Transactions ensure that a series of operations are treated as a single unit. If one operation fails, none of the operations will be applied to the database.</p>
</li>
<li><p><strong>Consistency</strong>: Transactions help maintain the integrity of the database by ensuring that all rules and constraints are followed.</p>
</li>
<li><p><strong>Isolation</strong>: Each transaction operates independently of others, preventing unintended interference.</p>
</li>
<li><p><strong>Durability</strong>: Once a transaction is committed, the changes are permanent, even in the event of a system failure.</p>
</li>
</ol>
<h3 id="heading-when-to-use-transactions">When to Use Transactions?</h3>
<p>You should use transactions when:</p>
<ul>
<li><p>Performing multiple related operations that must succeed or fail together.</p>
</li>
<li><p>Modifying critical data that requires consistency and integrity.</p>
</li>
<li><p>Working with operations that can potentially fail, such as financial transactions or data migrations.</p>
</li>
</ul>
<h3 id="heading-how-to-manage-transactions-in-python">How to Manage Transactions in Python</h3>
<p>In SQLite, transactions are managed using the <code>BEGIN</code>, <code>COMMIT</code>, and <code>ROLLBACK</code> commands. However, when using the <code>sqlite3</code> module in Python, you typically manage transactions through the connection object.</p>
<h5 id="heading-starting-a-transaction">Starting a Transaction</h5>
<p>A transaction begins implicitly when you execute any SQL statement. To start a transaction explicitly, you can use the <code>BEGIN</code> command:</p>
<pre><code class="lang-python">cursor.execute(<span class="hljs-string">"BEGIN;"</span>)
</code></pre>
<p>However, it’s usually unnecessary to start a transaction manually, as SQLite starts a transaction automatically when you execute an SQL statement.</p>
<h5 id="heading-how-to-commit-a-transaction">How to Commit a Transaction</h5>
<p>To save all changes made during a transaction, you use the <code>commit()</code> method. This makes all modifications permanent in the database.</p>
<pre><code class="lang-python">connection.commit()
</code></pre>
<p>We have already used the <code>commit()</code> method in the above provided examples.</p>
<h5 id="heading-rolling-back-a-transaction">Rolling Back a Transaction</h5>
<p>If something goes wrong and you want to revert the changes made during a transaction, you can use the <code>rollback()</code> method. This will undo all changes made since the transaction started.</p>
<pre><code class="lang-python">connection.rollback()
</code></pre>
<h3 id="heading-example-of-using-transactions-in-python">Example of Using Transactions in Python</h3>
<p>To illustrate the use of transactions in a real-world scenario, we’ll create a new table called <code>Customers</code> to manage customer accounts. In this example, we’ll assume each customer has a <code>balance</code>. We will add two customers to this table and perform a funds transfer operation between them.</p>
<p>First, let's create the <code>Customers</code> table and insert two customers:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3

<span class="hljs-comment"># Create the Customers table and add two customers</span>
<span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
    cursor = connection.cursor()

    <span class="hljs-comment"># Create Customers table</span>
    create_customers_table = <span class="hljs-string">'''
    CREATE TABLE IF NOT EXISTS Customers (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL UNIQUE,
        balance REAL NOT NULL
    );
    '''</span>
    cursor.execute(create_customers_table)

    <span class="hljs-comment"># Insert two customers</span>
    cursor.execute(
        <span class="hljs-string">"INSERT INTO Customers (name, balance) VALUES (?, ?);"</span>, (<span class="hljs-string">'Ashutosh'</span>, <span class="hljs-number">100.0</span>))
    cursor.execute(
        <span class="hljs-string">"INSERT INTO Customers (name, balance) VALUES (?, ?);"</span>, (<span class="hljs-string">'Krishna'</span>, <span class="hljs-number">50.0</span>))

    connection.commit()
</code></pre>
<p>Now, let’s perform the funds transfer operation between Ashutosh and Krishna:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">transfer_funds</span>(<span class="hljs-params">from_customer, to_customer, amount</span>):</span>
    <span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
        cursor = connection.cursor()

        <span class="hljs-keyword">try</span>:
            <span class="hljs-comment"># Start a transaction</span>
            cursor.execute(<span class="hljs-string">"BEGIN;"</span>)

            <span class="hljs-comment"># Deduct amount from the sender</span>
            cursor.execute(
                <span class="hljs-string">"UPDATE Customers SET balance = balance - ? WHERE name = ?;"</span>, (amount, from_customer))
            <span class="hljs-comment"># Add amount to the receiver</span>
            cursor.execute(
                <span class="hljs-string">"UPDATE Customers SET balance = balance + ? WHERE name = ?;"</span>, (amount, to_customer))

            <span class="hljs-comment"># Commit the changes</span>
            connection.commit()
            print(
                <span class="hljs-string">f"Transferred <span class="hljs-subst">{amount}</span> from <span class="hljs-subst">{from_customer}</span> to <span class="hljs-subst">{to_customer}</span>."</span>)

        <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
            <span class="hljs-comment"># If an error occurs, rollback the transaction</span>
            connection.rollback()
            print(<span class="hljs-string">f"Transaction failed: <span class="hljs-subst">{e}</span>"</span>)


<span class="hljs-comment"># Example usage</span>
transfer_funds(<span class="hljs-string">'Ashutosh'</span>, <span class="hljs-string">'Krishna'</span>, <span class="hljs-number">80.0</span>)
</code></pre>
<p>In this example, we first created a <code>Customers</code> table and inserted two customers, Ashutosh with a balance of ₹100, and Krishna with a balance of ₹50. We then performed a funds transfer of ₹80 from Ashutosh to Krishna. By using transactions, we ensure that both the debit from Ashutosh's account and the credit to Krishna's account are executed as a single atomic operation, maintaining data integrity in the event of any errors. If the transfer fails (for example, due to insufficient funds), the transaction will roll back, leaving both accounts unchanged.</p>
<h2 id="heading-how-to-optimize-sqlite-query-performance-with-indexing">How to Optimize SQLite Query Performance with Indexing</h2>
<p>Indexing is a powerful technique used in databases to improve query performance. An index is essentially a data structure that stores the location of rows based on specific column values, much like an index at the back of a book helps you quickly locate a topic.</p>
<p>Without an index, SQLite has to scan the entire table row by row to find the relevant data, which becomes inefficient as the dataset grows. By using an index, SQLite can jump directly to the rows you need, significantly speeding up query execution.</p>
<h3 id="heading-how-to-populate-the-database-with-fake-data">How to Populate the Database with Fake Data</h3>
<p>To effectively test the impact of indexing, we need a sizable dataset. Instead of manually adding records, we can use the <code>faker</code> library to quickly generate fake data. In this section, we’ll generate 10,000 fake records and insert them into our <code>Students</code> table. This will simulate a real-world scenario where databases grow large, and query performance becomes important.</p>
<p>We will use the <code>executemany()</code> method to insert the records as below:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3
<span class="hljs-keyword">from</span> faker <span class="hljs-keyword">import</span> Faker

<span class="hljs-comment"># Initialize the Faker library</span>
fake = Faker([<span class="hljs-string">'en_IN'</span>])


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">insert_fake_students</span>(<span class="hljs-params">num_records</span>):</span>
    <span class="hljs-string">"""Generate and insert fake student data into the Students table."""</span>
    fake_data = [(fake.name(), fake.random_int(min=<span class="hljs-number">18</span>, max=<span class="hljs-number">25</span>),
                  fake.email()) <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(num_records)]

    <span class="hljs-comment"># Use 'with' to handle the database connection</span>
    <span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
        cursor = connection.cursor()

        <span class="hljs-comment"># Insert fake data into the Students table</span>
        cursor.executemany(<span class="hljs-string">'''
        INSERT INTO Students (name, age, email) 
        VALUES (?, ?, ?);
        '''</span>, fake_data)

        connection.commit()

    print(<span class="hljs-string">f"<span class="hljs-subst">{num_records}</span> fake student records inserted successfully."</span>)


<span class="hljs-comment"># Insert 10,000 fake records into the Students table</span>
insert_fake_students(<span class="hljs-number">10000</span>)
</code></pre>
<p>By running this script, 10,000 fake student records will be added to the <code>Students</code> table. In the next section, we'll query the database and compare the performance of queries with and without indexing.</p>
<h3 id="heading-how-to-query-without-indexes">How to Query Without Indexes</h3>
<p>In this section, we’ll query the <code>Students</code> table without any indexes to observe how SQLite performs when there are no optimizations in place. This will serve as a baseline to compare the performance when we add indexes later.</p>
<p>Without indexes, SQLite performs a full table scan, which means that it must check every row in the table to find matching results. For small datasets, this is manageable, but as the number of records grows, the time taken to search increases dramatically. Let’s see this in action by running a basic <code>SELECT</code> query to search for a specific student by name and measure how long it takes.</p>
<p>First, we’ll query the <code>Students</code> table by looking for a student with a specific name. We’ll log the time taken to execute the query using Python’s <code>time</code> module to measure the performance.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3
<span class="hljs-keyword">import</span> time


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">query_without_index</span>(<span class="hljs-params">search_name</span>):</span>
    <span class="hljs-string">"""Query the Students table by name without an index and measure the time taken."""</span>

    <span class="hljs-comment"># Connect to the database using 'with'</span>
    <span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
        cursor = connection.cursor()

        <span class="hljs-comment"># Measure the start time</span>
        start_time = time.perf_counter_ns()

        <span class="hljs-comment"># Perform a SELECT query to find a student by name</span>
        cursor.execute(<span class="hljs-string">'''
        SELECT * FROM Students WHERE name = ?;
        '''</span>, (search_name,))

        <span class="hljs-comment"># Fetch all results (there should be only one or a few in practice)</span>
        results = cursor.fetchall()

        <span class="hljs-comment"># Measure the end time</span>
        end_time = time.perf_counter_ns()

        <span class="hljs-comment"># Calculate the total time taken</span>
        elapsed_time = (end_time - start_time) / <span class="hljs-number">1000</span>

        <span class="hljs-comment"># Display the results and the time taken</span>
        print(<span class="hljs-string">f"Query completed in <span class="hljs-subst">{elapsed_time:<span class="hljs-number">.5</span>f}</span> microseconds."</span>)
        print(<span class="hljs-string">"Results:"</span>, results)


<span class="hljs-comment"># Example: Searching for a student by name</span>
query_without_index(<span class="hljs-string">'Ojasvi Dhawan'</span>)
</code></pre>
<p>Here’s the output:</p>
<pre><code class="lang-bash">Query completed <span class="hljs-keyword">in</span> 1578.10000 microseconds.
Results: [(104, <span class="hljs-string">'Ojasvi Dhawan'</span>, 21, <span class="hljs-string">'lavanya26@example.com'</span>)]
</code></pre>
<p>By running the above script, you'll see how long it takes to search the <code>Students</code> table without any indexes. For example, if there are 10,000 records in the table, the query might take 1000-2000 microseconds depending on the size of the table and your hardware. This may not seem too slow for a small dataset, but the performance will degrade as more records are added.</p>
<p>We use <code>time.perf_counter_ns()</code> to measure the time taken for the query execution in nanoseconds. This method is highly accurate for benchmarking small time intervals. We convert the time to microseconds(<code>us</code>) for easier readability.</p>
<h3 id="heading-introducing-the-query-plan">Introducing the Query Plan</h3>
<p>When working with databases, understanding how queries are executed can help you identify performance bottlenecks and optimize your code. SQLite provides a helpful tool for this called <code>EXPLAIN QUERY PLAN</code>, which allows you to analyze the steps SQLite takes to retrieve data.</p>
<p>In this section, we’ll introduce how to use <code>EXPLAIN QUERY PLAN</code> to visualize and understand the inner workings of a query—specifically, how SQLite performs a full table scan when no index is present.</p>
<p>Let’s use <code>EXPLAIN QUERY PLAN</code> to see how SQLite retrieves data from the <code>Students</code> table without any indexes. We’ll search for a student by name, and the query plan will reveal the steps SQLite takes to find the matching rows.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">explain_query</span>(<span class="hljs-params">search_name</span>):</span>
    <span class="hljs-string">"""Explain the query execution plan for a SELECT query without an index."""</span>

    <span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
        cursor = connection.cursor()

        <span class="hljs-comment"># Use EXPLAIN QUERY PLAN to analyze how the query is executed</span>
        cursor.execute(<span class="hljs-string">'''
        EXPLAIN QUERY PLAN
        SELECT * FROM Students WHERE name = ?;
        '''</span>, (search_name,))

        <span class="hljs-comment"># Fetch and display the query plan</span>
        query_plan = cursor.fetchall()

        print(<span class="hljs-string">"Query Plan:"</span>)
        <span class="hljs-keyword">for</span> step <span class="hljs-keyword">in</span> query_plan:
            print(step)


<span class="hljs-comment"># Example: Analyzing the query plan for searching by name</span>
explain_query(<span class="hljs-string">'Ojasvi Dhawan'</span>)
</code></pre>
<p>When you run this code, SQLite will return a breakdown of how it plans to execute the query. Here’s an example of what the output might look like:</p>
<pre><code class="lang-bash">Query Plan:
(2, 0, 0, <span class="hljs-string">'SCAN Students'</span>)
</code></pre>
<p>This indicates that SQLite is scanning the entire <code>Students</code> table (a full table scan) to find the rows where the <code>name</code> column matches the provided value (<code>Ojasvi Dhawan</code>). Since there is no index on the <code>name</code> column, SQLite must examine each row in the table.</p>
<h3 id="heading-how-to-create-an-index">How to Create an Index</h3>
<p>Creating an index on a column allows SQLite to find rows more quickly during query operations. Instead of scanning the entire table, SQLite can use the index to jump directly to the relevant rows, significantly speeding up queries—especially those involving large datasets.</p>
<p>To create an index, use the following SQL command:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">INDEX</span> <span class="hljs-keyword">IF</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">EXISTS</span> <span class="hljs-keyword">index</span>-<span class="hljs-keyword">name</span> <span class="hljs-keyword">ON</span> <span class="hljs-keyword">table</span> (<span class="hljs-keyword">column</span>(s));
</code></pre>
<p>In this example, we will create an index on the <code>name</code> column of the <code>Students</code> table. Here’s how you can do it using Python:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3
<span class="hljs-keyword">import</span> time


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_index</span>():</span>
    <span class="hljs-string">"""Create an index on the name column of the Students table."""</span>
    <span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
        cursor = connection.cursor()

        <span class="hljs-comment"># SQL command to create an index on the name column</span>
        create_index_query = <span class="hljs-string">'''
        CREATE INDEX IF NOT EXISTS idx_name ON Students (name);
        '''</span>

        <span class="hljs-comment"># Measure the start time</span>
        start_time = time.perf_counter_ns()

        <span class="hljs-comment"># Execute the SQL command to create the index</span>
        cursor.execute(create_index_query)

        <span class="hljs-comment"># Measure the start time</span>
        end_time = time.perf_counter_ns()

        <span class="hljs-comment"># Commit the changes</span>
        connection.commit()

        print(<span class="hljs-string">"Index on 'name' column created successfully!"</span>)

        <span class="hljs-comment"># Calculate the total time taken</span>
        elapsed_time = (end_time - start_time) / <span class="hljs-number">1000</span>

        <span class="hljs-comment"># Display the results and the time taken</span>
        print(<span class="hljs-string">f"Query completed in <span class="hljs-subst">{elapsed_time:<span class="hljs-number">.5</span>f}</span> microseconds."</span>)


<span class="hljs-comment"># Call the function to create the index</span>
create_index()
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">Index on <span class="hljs-string">'name'</span> column created successfully!
Query completed <span class="hljs-keyword">in</span> 102768.60000 microseconds.
</code></pre>
<p>Even though creating the index takes this long (102768.6 microseconds), it's a one-time operation. You will still get substantial speed-up when running multiple queries. In the following sections, we will query the database again to observe the performance improvements made possible by this index.</p>
<h3 id="heading-how-to-query-with-indexes">How to Query with Indexes</h3>
<p>In this section, we will perform the same <code>SELECT</code> query we executed earlier, but this time we will take advantage of the index we created on the <code>name</code> column of the <code>Students</code> table. We'll measure and log the execution time to observe the performance improvements provided by the index.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3
<span class="hljs-keyword">import</span> time


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">query_with_index</span>(<span class="hljs-params">student_name</span>):</span>
    <span class="hljs-string">"""Query the Students table using an index on the name column."""</span>
    <span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
        cursor = connection.cursor()

        <span class="hljs-comment"># SQL command to select a student by name</span>
        select_query = <span class="hljs-string">'SELECT * FROM Students WHERE name = ?;'</span>

        <span class="hljs-comment"># Measure the execution time</span>
        start_time = time.perf_counter_ns()  <span class="hljs-comment"># Start the timer</span>

        <span class="hljs-comment"># Execute the query with the provided student name</span>
        cursor.execute(select_query, (student_name,))
        result = cursor.fetchall()  <span class="hljs-comment"># Fetch all results</span>

        end_time = time.perf_counter_ns()  <span class="hljs-comment"># End the timer</span>

        <span class="hljs-comment"># Calculate the elapsed time in microseconds</span>
        execution_time = (end_time - start_time) / <span class="hljs-number">1000</span>

        <span class="hljs-comment"># Display results and execution time</span>
        print(<span class="hljs-string">f"Query result: <span class="hljs-subst">{result}</span>"</span>)
        print(<span class="hljs-string">f"Execution time with index: <span class="hljs-subst">{execution_time:<span class="hljs-number">.5</span>f}</span> microseconds"</span>)


<span class="hljs-comment"># Example: Searching for a student by name</span>
query_with_index(<span class="hljs-string">'Ojasvi Dhawan'</span>)
</code></pre>
<p>Here’s what we get in the output:</p>
<pre><code class="lang-bash">Query result: [(104, <span class="hljs-string">'Ojasvi Dhawan'</span>, 21, <span class="hljs-string">'lavanya26@example.com'</span>)]
Execution time with index: 390.70000 microseconds
</code></pre>
<p>We can observe a significant reduction in execution time compared to when the query was performed without an index.</p>
<p>Let’s analyze the query execution plan for the query with the index on the <code>name</code> column of the <code>Students</code> table. If you execute the same script again to explain the query, you’ll get the below output:</p>
<pre><code class="lang-bash">Query Plan:
(3, 0, 0, <span class="hljs-string">'SEARCH Students USING INDEX idx_name (name=?)'</span>)
</code></pre>
<p>The plan now shows that the query uses the index <code>idx_name</code>, significantly reducing the number of rows that need to be scanned, which leads to faster query execution.</p>
<h3 id="heading-comparing-performance-results">Comparing Performance Results</h3>
<p>Now, let's summarize the performance results we obtained when querying with and without indexes.</p>
<h4 id="heading-execution-time-comparison">Execution Time Comparison</h4>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Query Type</td><td>Execution Time (microseconds)</td></tr>
</thead>
<tbody>
<tr>
<td>Without Index</td><td>1578.1</td></tr>
<tr>
<td>With Index</td><td>390.7</td></tr>
</tbody>
</table>
</div><h4 id="heading-performance-improvement-summary">Performance Improvement Summary</h4>
<ul>
<li><p>The query with the index is approximately 4.04 times faster than the query without the index.</p>
</li>
<li><p>The execution time improved by about 75.24% after adding the index.</p>
</li>
</ul>
<h3 id="heading-best-practices-for-using-indexes">Best Practices for Using Indexes</h3>
<p>Indexes can significantly enhance the performance of your SQLite database, but they should be used judiciously. Here are some best practices to consider when working with indexes:</p>
<h4 id="heading-when-and-why-to-use-indexes">When and Why to Use Indexes</h4>
<ol>
<li><p><strong>Frequent Query Columns</strong>: Use indexes on columns that are frequently used in <code>SELECT</code> queries, especially those used in <code>WHERE</code>, <code>JOIN</code>, and <code>ORDER BY</code> clauses. This is because indexing these columns can drastically reduce query execution time.</p>
</li>
<li><p><strong>Uniqueness Constraints</strong>: When you have columns that must hold unique values (like usernames or email addresses), creating an index can enforce this constraint efficiently.</p>
</li>
<li><p><strong>Large Datasets</strong>: For tables with a large number of records, indexes become increasingly beneficial. They enable quick lookups, which is essential for maintaining performance as your data grows.</p>
</li>
<li><p><strong>Composite Indexes</strong>: Consider creating composite indexes for queries that filter or sort by multiple columns. For example, if you often search for students by both <code>name</code> and <code>age</code>, an index on both columns can optimize such queries.</p>
</li>
</ol>
<h4 id="heading-potential-downsides-of-indexes">Potential Downsides of Indexes</h4>
<p>While indexes provide significant advantages, there are some potential downsides:</p>
<ol>
<li><p><strong>Slower Insert/Update Operations</strong>: When you insert or update records in a table with indexes, SQLite must also update the index, which can slow down these operations. This is because each insert or update requires additional overhead to maintain the index structure.</p>
</li>
<li><p><strong>Increased Storage Requirements</strong>: Indexes consume additional disk space. For large tables, the storage cost can be substantial. Consider this when designing your database schema, especially for systems with limited storage resources.</p>
</li>
<li><p><strong>Complex Index Management</strong>: Having too many indexes can complicate database management. It may lead to situations where you have redundant indexes, which can degrade performance rather than enhance it. Regularly reviewing and optimizing your indexes is a good practice.</p>
</li>
</ol>
<p>Indexes are powerful tools for optimizing database queries, but they require careful consideration. Striking a balance between improved read performance and the potential overhead on write operations is key. Here are some strategies for achieving this balance:</p>
<ul>
<li><p><strong>Monitor Query Performance</strong>: Use SQLite’s <code>EXPLAIN QUERY PLAN</code> to analyze how your queries perform with and without indexes. This can help identify which indexes are beneficial and which may be unnecessary.</p>
</li>
<li><p><strong>Regular Maintenance</strong>: Periodically review your indexes and assess whether they are still needed. Remove redundant or rarely used indexes to streamline your database operations.</p>
</li>
<li><p><strong>Test and Evaluate</strong>: Before implementing indexes in a production environment, conduct thorough testing to understand their impact on both read and write operations.</p>
</li>
</ul>
<p>By following these best practices, you can leverage the benefits of indexing while minimizing potential drawbacks, ultimately enhancing the performance and efficiency of your SQLite database.</p>
<h2 id="heading-how-to-handle-errors-and-exceptions">How to Handle Errors and Exceptions</h2>
<p>In this section, we’ll discuss how to handle errors and exceptions when working with SQLite in Python. Proper error handling is crucial for maintaining the integrity of your database and ensuring that your application behaves predictably.</p>
<h3 id="heading-common-errors-in-sqlite-operations">Common Errors in SQLite Operations</h3>
<p>When interacting with an SQLite database, several common errors may arise:</p>
<ol>
<li><p><strong>Constraint Violations</strong>: This occurs when you try to insert or update data that violates a database constraint, such as primary key uniqueness or foreign key constraints. For example, trying to insert a duplicate primary key will trigger an error.</p>
</li>
<li><p><strong>Data Type Mismatches</strong>: Attempting to insert data of the wrong type (for example, inserting a string where a number is expected) can lead to an error.</p>
</li>
<li><p><strong>Database Locked Errors</strong>: If a database is being written to by another process or connection, trying to access it can result in a "database is locked" error.</p>
</li>
<li><p><strong>Syntax Errors</strong>: Mistakes in your SQL syntax will result in errors when you try to execute your commands.</p>
</li>
</ol>
<h3 id="heading-how-to-use-pythons-exception-handling">How to Use Python's Exception Handling</h3>
<p>Python’s built-in <a target="_blank" href="https://blog.ashutoshkrris.in/exception-handling-in-python">exception handling</a> mechanisms (<code>try</code> and <code>except</code>) are essential for managing errors in SQLite operations. By using these constructs, you can catch exceptions and respond appropriately without crashing your program.</p>
<p>Here’s a basic example of how to handle errors when inserting data into the database:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add_customer_with_error_handling</span>(<span class="hljs-params">name, balance</span>):</span>
    <span class="hljs-string">"""Add a new customer with error handling."""</span>
    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
            cursor = connection.cursor()
            cursor.execute(
                <span class="hljs-string">"INSERT INTO Customers (name, balance) VALUES (?, ?);"</span>, (name, balance))
            connection.commit()
            print(<span class="hljs-string">f"Added customer: <span class="hljs-subst">{name}</span> with balance: <span class="hljs-subst">{balance}</span>"</span>)

    <span class="hljs-keyword">except</span> sqlite3.IntegrityError <span class="hljs-keyword">as</span> e:
        print(<span class="hljs-string">f"Error: Integrity constraint violated - <span class="hljs-subst">{e}</span>"</span>)

    <span class="hljs-keyword">except</span> sqlite3.OperationalError <span class="hljs-keyword">as</span> e:
        print(<span class="hljs-string">f"Error: Operational issue - <span class="hljs-subst">{e}</span>"</span>)

    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        print(<span class="hljs-string">f"An unexpected error occurred: <span class="hljs-subst">{e}</span>"</span>)


<span class="hljs-comment"># Example usage</span>
add_customer_with_error_handling(<span class="hljs-string">'Vishakha'</span>, <span class="hljs-number">100.0</span>)  <span class="hljs-comment"># Valid</span>
add_customer_with_error_handling(<span class="hljs-string">'Vishakha'</span>, <span class="hljs-number">150.0</span>)  <span class="hljs-comment"># Duplicate entry</span>
</code></pre>
<p>In this example:</p>
<ul>
<li><p>We catch <code>IntegrityError</code>, which is raised for violations like unique constraints.</p>
</li>
<li><p>We catch <code>OperationalError</code> for general database-related issues (like database locked errors).</p>
</li>
<li><p>We also have a generic <code>except</code> block to handle any unexpected exceptions.</p>
</li>
</ul>
<p>Output:</p>
<pre><code class="lang-bash">Added customer: Vishakha with balance: 100.0
Error: Integrity constraint violated - UNIQUE constraint failed: Customers.name
</code></pre>
<h3 id="heading-best-practices-for-ensuring-database-integrity">Best Practices for Ensuring Database Integrity</h3>
<ol>
<li><p><strong>Use Transactions</strong>: Always use transactions (as discussed in the previous section) when performing multiple related operations. This helps ensure that either all operations succeed or none do, maintaining consistency.</p>
</li>
<li><p><strong>Validate Input Data</strong>: Before executing SQL commands, validate the input data to ensure it meets the expected criteria (for example, correct types, within allowable ranges).</p>
</li>
<li><p><strong>Catch Specific Exceptions</strong>: Always catch specific exceptions to handle different types of errors appropriately. This allows for clearer error handling and debugging.</p>
</li>
<li><p><strong>Log Errors</strong>: Instead of just printing errors to the console, consider logging them to a file or monitoring system. This will help you track issues in production.</p>
</li>
<li><p><strong>Graceful Degradation</strong>: Design your application to handle errors gracefully. If an operation fails, provide meaningful feedback to the user rather than crashing the application.</p>
</li>
<li><p><strong>Regularly Backup Data</strong>: Regularly back up your database to prevent data loss in case of critical failures or corruption.</p>
</li>
<li><p><strong>Use Prepared Statements</strong>: Prepared statements help prevent SQL injection attacks and can also provide better performance for repeated queries.</p>
</li>
</ol>
<h2 id="heading-how-to-export-and-import-data-bonus-section">How to Export and Import Data [Bonus Section]</h2>
<p>In this section, we will learn how to export data from an SQLite database to common formats like CSV and JSON, as well as how to import data into SQLite from these formats using Python. This is useful for data sharing, backup, and integration with other applications.</p>
<h3 id="heading-exporting-data-from-sqlite-to-csv">Exporting Data from SQLite to CSV</h3>
<p>Exporting data to a CSV (Comma-Separated Values) file is straightforward with Python’s built-in libraries. CSV files are widely used for data storage and exchange, making them a convenient format for exporting data.</p>
<p>Here’s how to export data from an SQLite table to a CSV file:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3
<span class="hljs-keyword">import</span> csv

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">export_to_csv</span>(<span class="hljs-params">file_name</span>):</span>
    <span class="hljs-string">"""Export data from the Customers table to a CSV file."""</span>
    <span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
        cursor = connection.cursor()

        <span class="hljs-comment"># Execute a query to fetch all customer data</span>
        cursor.execute(<span class="hljs-string">"SELECT * FROM Customers;"</span>)
        customers = cursor.fetchall()

        <span class="hljs-comment"># Write data to CSV</span>
        <span class="hljs-keyword">with</span> open(file_name, <span class="hljs-string">'w'</span>, newline=<span class="hljs-string">''</span>) <span class="hljs-keyword">as</span> csv_file:
            csv_writer = csv.writer(csv_file)
            csv_writer.writerow([<span class="hljs-string">'ID'</span>, <span class="hljs-string">'Name'</span>, <span class="hljs-string">'Balance'</span>])  <span class="hljs-comment"># Writing header</span>
            csv_writer.writerows(customers)  <span class="hljs-comment"># Writing data rows</span>

        print(<span class="hljs-string">f"Data exported successfully to <span class="hljs-subst">{file_name}</span>."</span>)

<span class="hljs-comment"># Example usage</span>
export_to_csv(<span class="hljs-string">'customers.csv'</span>)
</code></pre>
<h3 id="heading-how-to-export-data-to-json">How to Export Data to JSON</h3>
<p>Similarly, you can export data to a <a target="_blank" href="https://blog.ashutoshkrris.in/a-beginners-guide-to-the-json-module-in-python">JSON</a> (JavaScript Object Notation) file, which is a popular format for data interchange, especially in web applications.</p>
<p>Here’s an example of how to export data to JSON:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> sqlite3


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">export_to_json</span>(<span class="hljs-params">file_name</span>):</span>
    <span class="hljs-string">"""Export data from the Customers table to a JSON file."""</span>
    <span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
        cursor = connection.cursor()

        <span class="hljs-comment"># Execute a query to fetch all customer data</span>
        cursor.execute(<span class="hljs-string">"SELECT * FROM Customers;"</span>)
        customers = cursor.fetchall()

        <span class="hljs-comment"># Convert data to a list of dictionaries</span>
        customers_list = [{<span class="hljs-string">'ID'</span>: customer[<span class="hljs-number">0</span>], <span class="hljs-string">'Name'</span>: customer[<span class="hljs-number">1</span>],
                           <span class="hljs-string">'Balance'</span>: customer[<span class="hljs-number">2</span>]} <span class="hljs-keyword">for</span> customer <span class="hljs-keyword">in</span> customers]

        <span class="hljs-comment"># Write data to JSON</span>
        <span class="hljs-keyword">with</span> open(file_name, <span class="hljs-string">'w'</span>) <span class="hljs-keyword">as</span> json_file:
            json.dump(customers_list, json_file, indent=<span class="hljs-number">4</span>)

        print(<span class="hljs-string">f"Data exported successfully to <span class="hljs-subst">{file_name}</span>."</span>)


<span class="hljs-comment"># Example usage</span>
export_to_json(<span class="hljs-string">'customers.json'</span>)
</code></pre>
<h3 id="heading-how-to-import-data-into-sqlite-from-csv">How to Import Data into SQLite from CSV</h3>
<p>You can also import data from a CSV file into an SQLite database. This is useful for populating your database with existing datasets.</p>
<p>Here's how to import data from a CSV file:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> csv
<span class="hljs-keyword">import</span> sqlite3


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">import_from_csv</span>(<span class="hljs-params">file_name</span>):</span>
    <span class="hljs-string">"""Import data from a CSV file into the Customers table."""</span>
    <span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
        cursor = connection.cursor()

        <span class="hljs-comment"># Open the CSV file for reading</span>
        <span class="hljs-keyword">with</span> open(file_name, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> csv_file:
            csv_reader = csv.reader(csv_file)
            next(csv_reader)  <span class="hljs-comment"># Skip the header row</span>

            <span class="hljs-comment"># Insert each row into the Customers table</span>
            <span class="hljs-keyword">for</span> row <span class="hljs-keyword">in</span> csv_reader:
                cursor.execute(
                    <span class="hljs-string">"INSERT INTO Customers (name, balance) VALUES (?, ?);"</span>, (row[<span class="hljs-number">1</span>], row[<span class="hljs-number">2</span>]))

        connection.commit()
        print(<span class="hljs-string">f"Data imported successfully from <span class="hljs-subst">{file_name}</span>."</span>)


<span class="hljs-comment"># Example usage</span>
import_from_csv(<span class="hljs-string">'customer_data.csv'</span>)
</code></pre>
<h3 id="heading-how-to-import-data-into-sqlite-from-json">How to Import Data into SQLite from JSON</h3>
<p>Similarly, importing data from a JSON file is simple. You can read the JSON file and insert the data into your SQLite table.</p>
<p>Here's how to do it:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> sqlite3


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">import_from_json</span>(<span class="hljs-params">file_name</span>):</span>
    <span class="hljs-string">"""Import data from a JSON file into the Customers table."""</span>
    <span class="hljs-keyword">with</span> sqlite3.connect(<span class="hljs-string">'my_database.db'</span>) <span class="hljs-keyword">as</span> connection:
        cursor = connection.cursor()

        <span class="hljs-comment"># Open the JSON file for reading</span>
        <span class="hljs-keyword">with</span> open(file_name, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> json_file:
            customers_list = json.load(json_file)

            <span class="hljs-comment"># Insert each customer into the Customers table</span>
            <span class="hljs-keyword">for</span> customer <span class="hljs-keyword">in</span> customers_list:
                cursor.execute(<span class="hljs-string">"INSERT INTO Customers (name, balance) VALUES (?, ?);"</span>, (customer[<span class="hljs-string">'Name'</span>], customer[<span class="hljs-string">'Balance'</span>]))

        connection.commit()
        print(<span class="hljs-string">f"Data imported successfully from <span class="hljs-subst">{file_name}</span>."</span>)


<span class="hljs-comment"># Example usage</span>
import_from_json(<span class="hljs-string">'customer_data.json'</span>)
</code></pre>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>And that’s a wrap! This guide has introduced you to the fundamentals of working with SQLite in Python, covering everything from setting up your environment to querying and manipulating data, as well as exporting and importing information. I hope you found it helpful and that it has sparked your interest in using SQLite for your projects.</p>
<p>Now it's time to put your newfound knowledge into practice! I encourage you to create your project using SQLite and Python. Whether it’s a simple application for managing your library, a budgeting tool, or something unique, the possibilities are endless.</p>
<p>Once you’ve completed your project, share it on Twitter and tag me! I’d love to see what you’ve created and celebrate your accomplishments.</p>
<p>You can find all the code from this tutorial on <a target="_blank" href="https://github.com/ashutoshkrris/sqlite-tutorial">GitHub</a>. Thank you for following along, and happy coding!</p>
<blockquote>
<p>Generate Table of Contents for your freeCodeCamp articles for free using the <a target="_blank" href="https://toc-generator.ashutoshkrris.in/freecodecamp">TOC Generator</a> tool.</p>
</blockquote>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Read and Write Files with Node.js ]]>
                </title>
                <description>
                    <![CDATA[ Node.js is a powerful JavaScript runtime environment that lets you run JS code outside the browser. And a fundamental part of many Node.js applications involves reading and writing files – whether that's text, JSON, HTML, or other file formats. So yo... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-read-and-write-files-with-nodejs/</link>
                <guid isPermaLink="false">66c34b90a7aea9fc97bdfb33</guid>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Mon, 19 Aug 2024 13:41:36 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1723804956795/2dbd964a-00c3-4489-819a-393b058ed1fd.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Node.js is a powerful JavaScript runtime environment that lets you run JS code outside the browser. And a fundamental part of many Node.js applications involves reading and writing files – whether that's text, JSON, HTML, or other file formats. So you should understand how to read and write files. </p>
<p>Files are the backbone of data storage. Node.js provides a powerful 'fs' (file system) module to interact with these files seamlessly. Let's assume I want to read a JSON file in Node.js. The fs module can help me with that.</p>
<p>In this tutorial, I'll explain the core functionalities of this module, explore various techniques to read different file types, and discover some best practices to make your file-handling operations smoother and more efficient.</p>
<p>Throughout this tutorial, we'll cover everything from importing the package to using it to work with files asynchronously. Let's get started on this journey of learning file operations with Node.js!</p>
<h3 id="heading-table-of-contents">Table of Contents</h3>
<ul>
<li><p><a class="post-section-overview" href="#heading-nodejs-fs-module">Node.js fs Module</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-read-and-write-files-with-nodejs">How to Read And Write Files With Node.js</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-ways-to-read-files-in-nodejs">Ways to Read Files in Node.js</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-nodejs-fs-module">Node.js <code>fs</code> Module</h2>
<p>The Node.js File System (fs) module is an essential component of the Node.js runtime environment. It provides a variety of features for interacting with your computer's file system.</p>
<p>The fs module allows you to read, write, update, delete, and manage files and directories. This module is especially useful for handling file-related operations in both synchronous and asynchronous modes.</p>
<p>Let’s break down the key aspects of the module:</p>
<ol>
<li><p>The fs module, at its core, provides a collection of APIs for interacting with the file system. It provides ways to perform basic activities such as reading file contents, writing data to files, creating directories, deleting files, and so on.</p>
</li>
<li><p>The module includes both synchronous and asynchronous methods for interacting with files. The synchronous methods block the execution of the program until the operation completes. But the asynchronous methods are ideal for scenarios where we need to perform concurrent tasks without halting the execution of the entire program.</p>
</li>
<li><p>The module also supports handling directories, such as creating directories, removing directories, and listing directory contents.</p>
</li>
<li><p>The module also supports working with file streams, allowing efficient handling of large files by reading or writing data in chunks without loading the entire content into memory. It also facilitates the use of buffers for handling binary data, which helps with activities like data transformation and manipulation.</p>
</li>
</ol>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To continue with the tutorial, I recommend having the following prerequisites:</p>
<ol>
<li><p><strong>Basic Understanding of JavaScript</strong>: It is essential to be familiar with JavaScript, as Node.js uses JavaScript.</p>
</li>
<li><p><strong>Node.js Installed</strong>: Ensure that Node.js is installed on your system. You can download and install Node.js from its <a target="_blank" href="https://nodejs.org">official website</a>. </p>
</li>
<li><p><strong>Text Editor/IDE</strong>: Have a text editor or an Integrated Development Environment (IDE) installed and ready to use.</p>
</li>
</ol>
<h2 id="heading-how-to-read-and-write-files-with-nodejs">How to Read And Write Files With Node.js</h2>
<p>Let's look at an example to understand the process of reading and writing files in Node.js. We'll assume a scenario where we have two files – name.json and address.json.</p>
<p>The content inside the name.json looks like this:</p>
<pre><code class="lang-json">[
  { <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Alice"</span> },
  { <span class="hljs-attr">"id"</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Bob"</span> },
  { <span class="hljs-attr">"id"</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Charlie"</span> }
]
</code></pre>
<p>The content inside address.json looks like this:</p>
<pre><code class="lang-json">[
  { <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">"address"</span>: <span class="hljs-string">"123 Main St"</span> },
  { <span class="hljs-attr">"id"</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">"address"</span>: <span class="hljs-string">"456 Elm St"</span> },
  { <span class="hljs-attr">"id"</span>: <span class="hljs-number">3</span>, <span class="hljs-attr">"address"</span>: <span class="hljs-string">"789 Oak St"</span> }
]
</code></pre>
<p>Our objective is to create a bio.json file that merges the id, name, and address information, creating a structure as follows:</p>
<pre><code class="lang-json">[
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Alice"</span>,
    <span class="hljs-attr">"address"</span>: <span class="hljs-string">"123 Main St"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">2</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Bob"</span>,
    <span class="hljs-attr">"address"</span>: <span class="hljs-string">"456 Elm St"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">3</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Charlie"</span>,
    <span class="hljs-attr">"address"</span>: <span class="hljs-string">"789 Oak St"</span>
  }
]
</code></pre>
<p>Let's build the application!</p>
<h3 id="heading-step-1-import-nodejs-packages-path-and-fs">Step 1: Import Node.js Packages <code>path</code> and <code>fs</code></h3>
<p>Let us start with creating an app.js file. The first thing we will do is import the fs library:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);
</code></pre>
<h3 id="heading-step-2-read-from-files">Step 2: Read From Files</h3>
<p>Next, let's read data from the two files using Node.js. We'll make a utility function that helps us read these files easily in our Node.js environment.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">readJSONFile</span>(<span class="hljs-params">filename</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> fs.readFile(filename, <span class="hljs-string">"utf8"</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.parse(data);
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">`Error reading <span class="hljs-subst">${filename}</span>: <span class="hljs-subst">${error}</span>`</span>);
    <span class="hljs-keyword">return</span> [];
  }
}
</code></pre>
<p>Since we are using JSON files in our example, we have defined a readJSONFile method in our code. It's an asynchronous JavaScript function that takes a filename as input and aims to return the parsed JSON contents of that file.</p>
<p>Inside a try block, we attempt to read the file using fs.readFile in Node with the specified filename and "utf8" encoding. If successful, the function then parses the file content as JSON using JSON.parse and returns it.</p>
<p>If any error occurs during reading or parsing, the catch block takes over. It logs the error with the filename and details and then returns an empty array instead of the expected JSON object.</p>
<h3 id="heading-step-3-implement-the-main-function">Step 3: Implement the Main Function</h3>
<p>The next step is to create a main function where we make use of the above-defined method and combine the data of the two files to create a bio.json file.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> names = <span class="hljs-keyword">await</span> readJSONFile(<span class="hljs-string">"names.json"</span>);
    <span class="hljs-keyword">const</span> addresses = <span class="hljs-keyword">await</span> readJSONFile(<span class="hljs-string">"address.json"</span>);

    <span class="hljs-keyword">const</span> bioData = names.map(<span class="hljs-function">(<span class="hljs-params">name</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> matchingAddress = addresses.find(
        <span class="hljs-function">(<span class="hljs-params">address</span>) =&gt;</span> address.id === name.id
      );
      <span class="hljs-keyword">return</span> { ...name, ...matchingAddress };
    });

    <span class="hljs-keyword">await</span> fs.writeFile(<span class="hljs-string">"bio.json"</span>, <span class="hljs-built_in">JSON</span>.stringify(bioData, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>));
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"bio.json created successfully!"</span>);
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error combining data:"</span>, error);
  }
}
</code></pre>
<p>In the main function, we first read the two JSON files named <code>names.json</code> and <code>address.json</code> using the readJSONFile function. Both readJSONFile calls use await, so the function waits for both files to be read before moving on.</p>
<p>Next, we iterate through each <code>name</code> using a map, creating a new <code>bioData</code> for each. Inside the loop, it searches for a matching <code>address</code> from the addresses collection based on the index using find. </p>
<p>The search compares <code>name.id</code> with each <code>address.id</code> until it finds a match. If a match is found, the function combines the information from both files. It uses the spread operator (...) to merge all properties from both objects into a single new <code>bioData</code> object. If no matching address is found, the <code>bioData</code> object will only have the name information.</p>
<p>Once all <code>bioData</code> objects are prepared, the function writes them as a new JSON file named <code>bio.json</code> using <code>fs.writeFile</code>. This writing process also uses await, ensuring the file is created before moving on. </p>
<p>The try block ensures smooth execution, while the catch block takes care of any errors like missing files or incorrect data. If any error occurs, a generic error message and the specific error details are logged for debugging.</p>
<h3 id="heading-complete-code">Complete Code</h3>
<p>Our completed code looks like below:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>).promises;

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">readJSONFile</span>(<span class="hljs-params">filename</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> fs.readFile(filename, <span class="hljs-string">"utf8"</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.parse(data);
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">`Error reading <span class="hljs-subst">${filename}</span>: <span class="hljs-subst">${error}</span>`</span>);
    <span class="hljs-keyword">return</span> [];
  }
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> names = <span class="hljs-keyword">await</span> readJSONFile(<span class="hljs-string">"names.json"</span>);
    <span class="hljs-keyword">const</span> addresses = <span class="hljs-keyword">await</span> readJSONFile(<span class="hljs-string">"address.json"</span>);

    <span class="hljs-keyword">const</span> bioData = names.map(<span class="hljs-function">(<span class="hljs-params">name</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> matchingAddress = addresses.find(
        <span class="hljs-function">(<span class="hljs-params">address</span>) =&gt;</span> address.id === name.id
      );
      <span class="hljs-keyword">return</span> { ...name, ...matchingAddress };
    });

    <span class="hljs-keyword">await</span> fs.writeFile(<span class="hljs-string">"bio.json"</span>, <span class="hljs-built_in">JSON</span>.stringify(bioData, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>));
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"bio.json created successfully!"</span>);
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error combining data:"</span>, error);
  }
}

<span class="hljs-comment">// Execute the main method</span>
main();
</code></pre>
<p>We can run the application using the following command:</p>
<pre><code class="lang-bash">node app.js
</code></pre>
<p>Once the application runs, we get to see the following logs in the terminal if everything goes well:</p>
<pre><code class="lang-bash">bio.json created successfully!
</code></pre>
<p>This indicates that the bio.json file was created successfully. The content inside the file should look like below:</p>
<pre><code class="lang-json">[
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Alice"</span>,
    <span class="hljs-attr">"address"</span>: <span class="hljs-string">"123 Main St"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">2</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Bob"</span>,
    <span class="hljs-attr">"address"</span>: <span class="hljs-string">"456 Elm St"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">3</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Charlie"</span>,
    <span class="hljs-attr">"address"</span>: <span class="hljs-string">"789 Oak St"</span>
  }
]
</code></pre>
<h2 id="heading-ways-to-read-files-in-nodejs">Ways to Read Files in Node.js</h2>
<p>To read files in Node.js, we mostly use two primary methods: <code>fs.readFile()</code> and <code>fs.readFileSync()</code>. The difference lies in their synchronous and asynchronous nature.</p>
<h3 id="heading-fsreadfile-method"><code>fs.readFile()</code> Method</h3>
<p>The <code>fs.readFile()</code> method in Node.js is asynchronous. It reads the content of the entire file without blocking other operations. This makes it suitable for scenarios where non-blocking I/O operations are essential.</p>
<p>In simple terms, the function allows other operations to continue while the reading takes place. It accepts the following three parameters:</p>
<ol>
<li><p>path: the path to the file to be read.</p>
</li>
<li><p>encoding: optional, specifies the encoding of the file (for example, "utf8"). Defaults to "utf8" if not provided.</p>
</li>
<li><p>callback function: optional, a function to be called when the file is read. The function receives three arguments: error, data, and buffer.</p>
</li>
</ol>
<p>Let’s look at an example:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);

fs.readFile(<span class="hljs-string">"data.txt"</span>, <span class="hljs-string">"utf8"</span>, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (err) {
    <span class="hljs-built_in">console</span>.error(err);
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">console</span>.log(data); <span class="hljs-comment">// data will be a string containing the content of the file</span>
  }
});
</code></pre>
<h3 id="heading-fsreadfilesync-method"><code>fs.readFileSync()</code> Method</h3>
<p>The <code>fs.readFileSync()</code> method is synchronous. It reads the file content synchronously, stopping further execution until the file is completely read. This method is beneficial in scenarios where we require synchronous processing.</p>
<p>It takes only two parameters:</p>
<ol>
<li><p>path: the path to the file to be read</p>
</li>
<li><p>encoding: optional, specifies the encoding of the file (for example, "utf8"). Defaults to "utf8" if not provided.</p>
</li>
</ol>
<p>Let’s look at an example:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);

<span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">const</span> data = fs.readFileSync(<span class="hljs-string">"data.txt"</span>, <span class="hljs-string">"utf8"</span>);
  <span class="hljs-built_in">console</span>.log(data); <span class="hljs-comment">// data will be a string containing the content of the file</span>
} <span class="hljs-keyword">catch</span> (err) {
  <span class="hljs-built_in">console</span>.error(err);
}
</code></pre>
<h3 id="heading-fsreadfile-vs-fsreadfilesync"><code>fs.readFile()</code> vs <code>fs.readFileSync()</code></h3>
<p>To understand the difference between the Nodejs <code>readFile</code> and <code>readFileSync</code> methods, we will write two programs and monitor their execution flow.</p>
<p>Let's start with the <code>fs.readFile()</code> method.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);

fs.readFile(<span class="hljs-string">"example.txt"</span>, <span class="hljs-string">"utf8"</span>, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Content from readFile:"</span>, data);
});

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Completed reading file content asynchronously"</span>);
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">Completed reading file content asynchronously
Content from readFile: freeCodeCamp is awesome!
</code></pre>
<p>Because of <code>fs.readFile()</code>'s asynchronous nature, the code after <code>fs.readFile()</code> doesn't wait for the file reading operation to finish. So the "Completed reading file content synchronously" message is immediately logged to the console, showing that the subsequent code continues executing without waiting for the file read to complete.</p>
<p>Eventually, when the file reading operation finishes, the callback function executes and logs the file content.</p>
<p>Next, let's see the execution of the <code>fs.readFileSync()</code> method.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);

<span class="hljs-keyword">const</span> data = fs.readFileSync(<span class="hljs-string">"data.txt"</span>, <span class="hljs-string">"utf8"</span>);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Content from readFileSync:"</span>, data);

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Completed reading file content synchronously"</span>);
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">Content from readFileSync: freeCodeCamp is awesome!
Completed reading file content synchronously
</code></pre>
<p>In contrast, <code>fs.readFileSync()</code> reads the data.txt file synchronously, blocking further code execution until the file is completely read. Consequently, the code continues execution only after the file reading operation is finished.</p>
<p>Because of this, the message "Completed reading file content synchronously" is logged after successfully reading the file content.</p>
<p>Now, we know the difference between the two methods. Understanding this difference is important as it impacts the program flow, especially in scenarios where timing and blocking operations are critical considerations in Node.js applications.</p>
<h3 id="heading-how-to-read-a-text-file">How to Read a Text File</h3>
<p>It's pretty straightforward to read a text file in Node.js, and we have been doing this throughout the tutorial. Let’s consider that we have a file called message.txt with the following content:</p>
<pre><code class="lang-plaintext">Learn Node.js with freeCodeCamp
</code></pre>
<p>Now, we want to read the contents of this file. We can do it like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);

fs.readFile(<span class="hljs-string">"message.txt"</span>, <span class="hljs-string">"utf8"</span>, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (err) {
    <span class="hljs-built_in">console</span>.log(err);
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">console</span>.log(data);
  }
});
</code></pre>
<p>The callback function returns the content of the file in a data variable. Since we set the encoding to “utf8”, the value of data is a string. So we can perform string operations on the data variable.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);

fs.readFile(<span class="hljs-string">"message.txt"</span>, <span class="hljs-string">"utf8"</span>, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (err) {
    <span class="hljs-built_in">console</span>.log(err);
  } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">let</span> splittedWords = data.split(<span class="hljs-string">" "</span>);
      <span class="hljs-built_in">console</span>.log(splittedWords);
  }
});
</code></pre>
<p>In the above code, we split the data variable using a space. So the splittedWords will be a string array containing the following value:</p>
<pre><code class="lang-javascript">[ <span class="hljs-string">'Learn'</span>, <span class="hljs-string">'Node.js'</span>, <span class="hljs-string">'with'</span>, <span class="hljs-string">'freeCodeCamp'</span> ]
</code></pre>
<h3 id="heading-how-to-read-html-files">How to Read HTML Files</h3>
<p>Reading HTML files follows a similar approach to reading text files in Node.js. We can use the <code>fs</code> module to read HTML files:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);

fs.readFile(<span class="hljs-string">"index.html"</span>, <span class="hljs-string">"utf8"</span>, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (err) {
    <span class="hljs-built_in">console</span>.log(err);
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">console</span>.log(data);
  }
});
</code></pre>
<p>We can then use the HTML content for further processing, such as rendering it with the Node.js <code>http</code> package.</p>
<h3 id="heading-how-to-read-files-by-url">How to Read Files by URL</h3>
<p>Reading files by URL in Node.js involves additional steps beyond the native fs module. Typically, we’ll need to use additional modules like <code>http</code> or <code>axios</code> to fetch file content from a URL.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);
<span class="hljs-keyword">const</span> https = <span class="hljs-built_in">require</span>(<span class="hljs-string">"https"</span>);

<span class="hljs-keyword">const</span> file = fs.createWriteStream(<span class="hljs-string">"data.txt"</span>);

https.get(
  <span class="hljs-string">"https://example-files.online-convert.com/document/txt/example.txt"</span>,
  <span class="hljs-function">(<span class="hljs-params">response</span>) =&gt;</span> {
    <span class="hljs-keyword">var</span> stream = response.pipe(file);

    stream.on(<span class="hljs-string">"finish"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"done"</span>);
    });
  }
);
</code></pre>
<p>First, we set up a writable stream named file, associated with the local file data.txt. We then use Node.js's <code>https</code> module to perform an HTTP GET request to the specified URL. The get method triggers a callback function when we receive a response from the server. </p>
<p>Inside the callback, the script pipes the response directly into the writable stream. This operation efficiently directs the data received from the remote server to the local file data.txt, essentially downloading and writing the content concurrently. </p>
<p>Finally, we set up an event listener for the "finish" event on the stream. This event fires when all the data has been successfully written to the file. Upon completion, the script logs "done" to the console, indicating the successful download and writing of the file.</p>
<h3 id="heading-how-to-read-a-json-file">How to Read a JSON File</h3>
<p>We have already seen how we can read a JSON file using the fs module. Let's say that we want to read our previously created bio.json file. Its data looks like the below:</p>
<pre><code class="lang-json">[
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Alice"</span>,
    <span class="hljs-attr">"address"</span>: <span class="hljs-string">"123 Main St"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">2</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Bob"</span>,
    <span class="hljs-attr">"address"</span>: <span class="hljs-string">"456 Elm St"</span>
  },
  {
    <span class="hljs-attr">"id"</span>: <span class="hljs-number">3</span>,
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Charlie"</span>,
    <span class="hljs-attr">"address"</span>: <span class="hljs-string">"789 Oak St"</span>
  }
]
</code></pre>
<p>In Node.js, we read JSON like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);

fs.readFile(<span class="hljs-string">"bio.json"</span>, <span class="hljs-string">"utf8"</span>, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (err) {
    <span class="hljs-built_in">console</span>.log(err);
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-built_in">console</span>.log(data);
  }
});
</code></pre>
<p>With this, our JSON data is stored within the data variable as a string. If we want, we can use it for further processing. Let’s say, we want to print the details of the user:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>);

fs.readFile(<span class="hljs-string">"bio.json"</span>, <span class="hljs-string">"utf8"</span>, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {
  <span class="hljs-keyword">if</span> (err) {
    <span class="hljs-built_in">console</span>.log(err);
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">const</span> users = <span class="hljs-built_in">JSON</span>.parse(data);
    users.forEach(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`<span class="hljs-subst">${user.name}</span> with ID <span class="hljs-subst">${user.id}</span> lives at <span class="hljs-subst">${user.address}</span>`</span>);
    });
  }
});
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">Alice with ID 1 lives at 123 Main St
Bob with ID 2 lives at 456 Elm St
Charlie with ID 3 lives at 789 Oak St
</code></pre>
<p>In the above code, we first parse the string data variable to JSON and store it in a <code>users</code> variable. We then loop over the <code>users</code> variable to log the required message.</p>
<h3 id="heading-fspromises"><code>fs.promises</code></h3>
<p><code>fs.promises</code> provides a set of asynchronous functions for interacting with the file system in Node.js. These functions are based on promises and offer a more readable and efficient way to handle asynchronous operations compared to callbacks.</p>
<p>With <code>fs.promises</code>, we don't need to add nested callbacks – which means that we can avoid callback hell.</p>
<p>A basic readFile operation with <code>fs.promises</code> looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">"fs"</span>).promises;

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">readTextFile</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> fs.readFile(<span class="hljs-string">"data.txt"</span>, <span class="hljs-string">"utf8"</span>);
    <span class="hljs-built_in">console</span>.log(data);
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.error(err);
  }
}

readTextFile();
</code></pre>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>In this tutorial, we've explored the essential techniques for handling files in Node.js with the help of the fs module.</p>
<p>From understanding synchronous and asynchronous file reading with methods like <code>fs.readFile()</code> and <code>fs.readFileSync()</code> to processing various file formats such as text, HTML, JSON, and even reading files from URLs, we've covered a bunch of functionalities. We also learned about fs.promises, which is a more elegant way to handle file operations using asynchronous functions.</p>
<h3 id="heading-frequently-asked-questions-faqs">Frequently Asked Questions (FAQs)</h3>
<h4 id="heading-1-how-do-i-read-a-file-asynchronously-in-nodejs">1. How do I read a file asynchronously in Node.js?</h4>
<p>There are mainly two ways to read a file asynchronously in Node.js: using <code>fs.readFile()</code> and using <code>fs.promises</code>.</p>
<p>The <code>fs.readFile()</code> method uses callbacks to handle the operation. We provide a callback function that will be called with the file data (or an error) when the reading is complete.</p>
<p>But <code>fs.promises</code> offers a promise-based approach. We can use the <code>readFile</code> function with await to wait for the reading to finish and then access the data directly.</p>
<h4 id="heading-2-how-can-i-check-if-a-file-exists-before-reading-or-writing-in-nodejs">2. How can I check if a file exists before reading or writing in Node.js?</h4>
<p>There are two methods to check if a file exist: using <code>fs.stat</code> and using <code>fs.promises.access</code>.</p>
<p>The <code>fs.stat</code> method synchronously checks if a file exists and returns information about it like size and access time. The <code>fs.promises.access</code> method asynchronously checks if a file exists and returns a promise that resolves or rejects based on the existence of the file.</p>
<h4 id="heading-3-how-can-i-handle-errors-when-reading-or-writing-files-in-nodejs">3. How can I handle errors when reading or writing files in Node.js?</h4>
<p>To handle errors while reading or writing files in Node.js, we can utilize error-first callbacks or promises along with try-catch blocks.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Comparable vs Comparator Interfaces in Java – Which Should You Use and When? ]]>
                </title>
                <description>
                    <![CDATA[ Sorting is a fundamental operation in programming, essential for organizing data in a specific order. In Java, built-in sorting methods provide efficient ways to sort primitive data types and arrays, making it easy to manage and manipulate collection... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/comparable-vs-comparator-explained-in-java/</link>
                <guid isPermaLink="false">66ba0e6e79b7f411df58dea3</guid>
                
                    <category>
                        <![CDATA[ algorithms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Java ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Tue, 23 Jul 2024 13:13:33 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/07/comparable-comparator.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Sorting is a fundamental operation in programming, essential for organizing data in a specific order. In Java, built-in sorting methods provide efficient ways to sort primitive data types and arrays, making it easy to manage and manipulate collections of data. For instance, you can quickly sort an array of integers or a list of strings using methods like <code>Arrays.sort()</code> and <code>Collections.sort()</code>. </p>
<p>However, when it comes to sorting custom objects, such as instances of user-defined classes, the built-in sorting methods fall short. These methods don't know how to order objects based on custom criteria. This is where Java's <code>Comparable</code> and <code>Comparator</code> interfaces come into play, allowing developers to define and implement custom sorting logic tailored to specific requirements.</p>
<p>In this blog post, we'll explore how to use the <code>Comparable</code> and <code>Comparator</code> interfaces to sort custom objects in Java. I'll provide examples to illustrate the differences and use cases for each approach, helping you master custom sorting in your Java applications.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-sorting-methods-for-primitive-types">Sorting Methods for Primitive Types</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-the-comparable-interface">How to Use the Comparable Interface</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-the-comparator-interface">How to Use the Comparator Interface</a></li>
<li><a class="post-section-overview" href="#heading-comparable-vs-comparator">Comparable vs Comparator</a></li>
<li><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></li>
</ul>
<h2 id="heading-sorting-methods-for-primitive-types">Sorting Methods for Primitive Types</h2>
<p>Java provides a variety of built-in sorting methods that make it easy to sort primitive data types. These methods are highly optimized and efficient, allowing you to sort arrays and collections with minimal code. For primitive types, such as integers, floating-point numbers, and characters, the <code>Arrays.sort()</code> method is commonly used.</p>
<h3 id="heading-how-to-use-the-arrayssort-method">How to Use the Arrays.sort() Method</h3>
<p>The <code>Arrays.sort()</code> method sorts the specified array into ascending numerical order. This method uses a dual-pivot quicksort algorithm, which is faster and more efficient for most data sets.</p>
<p>Let's look at an example of sorting an array of integers and characters using <code>Arrays.sort()</code>:</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> tutorial;

<span class="hljs-keyword">import</span> java.util.Arrays;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PrimitiveSorting</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>{
        <span class="hljs-keyword">int</span>[] numbers = { <span class="hljs-number">5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">8</span>, <span class="hljs-number">2</span>, <span class="hljs-number">1</span> };
        System.out.println(<span class="hljs-string">"Original array: "</span> + Arrays.toString(numbers));

        Arrays.sort(numbers);
        System.out.println(<span class="hljs-string">"Sorted array: "</span> + Arrays.toString(numbers));

        <span class="hljs-keyword">char</span>[] characters = { <span class="hljs-string">'o'</span>, <span class="hljs-string">'i'</span>, <span class="hljs-string">'e'</span>, <span class="hljs-string">'u'</span>, <span class="hljs-string">'a'</span> };
        System.out.println(<span class="hljs-string">"Original array: "</span> + Arrays.toString(characters));

        Arrays.sort(characters);
        System.out.println(<span class="hljs-string">"Sorted array: "</span> + Arrays.toString(characters));
    }
}
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">Original array: [5, 3, 8, 2, 1]
Sorted array: [1, 2, 3, 5, 8]
Original array: [o, i, e, u, a]
Sorted array: [a, e, i, o, u]
</code></pre>
<h3 id="heading-how-to-use-the-collectionssort-method">How to Use the Collections.sort() Method</h3>
<p>The <code>Collections.sort()</code> method is used to sort collections such as <code>ArrayList</code>. This method is also based on the natural ordering of the elements or a custom comparator.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> tutorial;

<span class="hljs-keyword">import</span> java.util.ArrayList;
<span class="hljs-keyword">import</span> java.util.Collections;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CollectionsSorting</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>{
        ArrayList&lt;String&gt; wordsList = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
        wordsList.add(<span class="hljs-string">"banana"</span>);
        wordsList.add(<span class="hljs-string">"apple"</span>);
        wordsList.add(<span class="hljs-string">"cherry"</span>);
        wordsList.add(<span class="hljs-string">"date"</span>);
        System.out.println(<span class="hljs-string">"Original list: "</span> + wordsList);

        Collections.sort(wordsList);
        System.out.println(<span class="hljs-string">"Sorted list: "</span> + wordsList);
    }
}
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Original list: [banana, apple, cherry, date]
Sorted list: [apple, banana, cherry, date]
</code></pre>
<h3 id="heading-limitations-with-custom-classes">Limitations with Custom Classes</h3>
<p>While Java's built-in sorting methods, such as <code>Arrays.sort()</code> and <code>Collections.sort()</code>, are powerful and efficient for sorting primitive types and objects with natural ordering (like <code>String</code>), they fall short when it comes to sorting custom objects. These methods do not inherently know how to order user-defined objects because there is no natural way for them to compare these objects.</p>
<p>For example, consider a simple <code>Person</code> class that has <code>name</code>, <code>age</code>, and <code>weight</code> attributes:</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> tutorial;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Person</span> </span>{
    String name;
    <span class="hljs-keyword">int</span> age;
    <span class="hljs-keyword">double</span> weight;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Person</span><span class="hljs-params">(String name, <span class="hljs-keyword">int</span> age, <span class="hljs-keyword">double</span> weight)</span> </span>{
        <span class="hljs-keyword">this</span>.name = name;
        <span class="hljs-keyword">this</span>.age = age;
        <span class="hljs-keyword">this</span>.weight = weight;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Person [name="</span> + name + <span class="hljs-string">", age="</span> + age + <span class="hljs-string">", weight="</span> + weight + <span class="hljs-string">" kgs]"</span>;
    }
}
</code></pre>
<p>If we try to sort a list of <code>Person</code> objects using <code>Arrays.sort()</code> or <code>Collections.sort()</code>, we will encounter a compilation error because these methods do not know how to compare <code>Person</code> objects:</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> tutorial;

<span class="hljs-keyword">import</span> java.util.ArrayList;
<span class="hljs-keyword">import</span> java.util.Arrays;
<span class="hljs-keyword">import</span> java.util.Collections;
<span class="hljs-keyword">import</span> java.util.List;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CustomClassSorting</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>{
        List&lt;Person&gt; people = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;(Arrays.asList(
                <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"Alice"</span>, <span class="hljs-number">30</span>, <span class="hljs-number">65.5</span>),
                <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"Bob"</span>, <span class="hljs-number">25</span>, <span class="hljs-number">75.0</span>),
                <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"Charlie"</span>, <span class="hljs-number">35</span>, <span class="hljs-number">80.0</span>)
        ));
        System.out.println(<span class="hljs-string">"Original people list: "</span> + people);

        Collections.sort(people);
        System.out.println(<span class="hljs-string">"Sorted people list: "</span> + people);
    }
}
</code></pre>
<p>Compilation Error:</p>
<pre><code class="lang-bash">java: no suitable method found <span class="hljs-keyword">for</span> sort(java.util.List&lt;tutorial.Person&gt;)
    method java.util.Collections.&lt;T&gt;sort(java.util.List&lt;T&gt;) is not applicable
      (inference variable T has incompatible bounds
        equality constraints: tutorial.Person
        lower bounds: java.lang.Comparable&lt;? super T&gt;)
    method java.util.Collections.&lt;T&gt;sort(java.util.List&lt;T&gt;,java.util.Comparator&lt;? super T&gt;) is not applicable
      (cannot infer type-variable(s) T
        (actual and formal argument lists differ <span class="hljs-keyword">in</span> length))
</code></pre>
<p>The error occurs because the <code>Person</code> class does not implement the <code>Comparable</code> interface, and there is no way for the sorting method to know how to compare two <code>Person</code> objects.</p>
<p>To sort custom objects like <code>Person</code>, we need to provide a way to compare these objects. Java offers two main approaches to achieve this:</p>
<ol>
<li>Implementing the <code>Comparable</code> Interface: This allows a class to define its natural ordering by implementing the <code>compareTo</code> method.</li>
<li>Using the <code>Comparator</code> Interface: This allows us to create separate classes or lambda expressions to define multiple ways of comparing objects.</li>
</ol>
<p>We will explore both approaches in the upcoming sections, starting with the <code>Comparable</code> interface.</p>
<h2 id="heading-how-to-use-the-comparable-interface">How to Use the Comparable Interface</h2>
<p>Java provides a <code>Comparable</code> interface to define a natural ordering for objects of a user-defined class. By implementing the <code>Comparable</code> interface, a class can provide a single natural ordering that can be used to sort its instances. This is particularly useful when you need a default way to compare and sort objects.</p>
<h3 id="heading-overview">Overview</h3>
<p>The <code>Comparable</code> interface contains a single method, <code>compareTo()</code>, which compares the current object with the specified object for order. The method returns:</p>
<ul>
<li>A negative integer if the current object is less than the specified object.</li>
<li>Zero if the current object is equal to the specified object.</li>
<li>A positive integer if the current object is greater than the specified object.</li>
</ul>
<h3 id="heading-how-comparable-allows-for-a-single-natural-ordering-of-objects">How Comparable Allows for a Single Natural Ordering of Objects</h3>
<p>By implementing the <code>Comparable</code> interface, a class can ensure that its objects have a natural ordering. This allows the objects to be sorted using methods like <code>Arrays.sort()</code> or <code>Collections.sort()</code> without the need for a separate comparator.</p>
<p>Let's implement the <code>Comparable</code> interface in a new <code>PersonV2</code> class, comparing by age.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> tutorial;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PersonV2</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Comparable</span>&lt;<span class="hljs-title">PersonV2</span>&gt; </span>{
    String name;
    <span class="hljs-keyword">int</span> age;
    <span class="hljs-keyword">double</span> weight;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">PersonV2</span><span class="hljs-params">(String name, <span class="hljs-keyword">int</span> age, <span class="hljs-keyword">double</span> weight)</span> </span>{
        <span class="hljs-keyword">this</span>.name = name;
        <span class="hljs-keyword">this</span>.age = age;
        <span class="hljs-keyword">this</span>.weight = weight;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"PersonV2 [name="</span> + name + <span class="hljs-string">", age="</span> + age + <span class="hljs-string">", weight="</span> + weight + <span class="hljs-string">" kgs]"</span>;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">compareTo</span><span class="hljs-params">(PersonV2 other)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.age - other.age;
    }
}
</code></pre>
<p>In this implementation, the <code>compareTo()</code> method compares the <code>age</code> attribute of the current <code>PersonV2</code> object with the <code>age</code> attribute of the specified <code>PersonV2</code> object by subtracting one age from the other. By using the expression <code>this.age - other.age</code>, we’re effectively implementing this logic as follows:</p>
<ul>
<li>If <code>this.age</code> is less than <code>other.age</code>, the result will be negative.</li>
<li>If <code>this.age</code> is equal to <code>other.age</code>, the result will be zero.</li>
<li>If <code>this.age</code> is greater than <code>other.age</code>, the result will be positive.</li>
</ul>
<p><strong>Note</strong>: We can also use <code>Integer.compare(this.age, other.age)</code> instead of performing the arithmetic operation manually.</p>
<p>Now that the <code>PersonV2</code> class implements the <code>Comparable</code> interface, we can sort a list of <code>PersonV2</code> objects using <code>Collections.sort()</code>:</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> tutorial;

<span class="hljs-keyword">import</span> java.util.ArrayList;
<span class="hljs-keyword">import</span> java.util.Arrays;
<span class="hljs-keyword">import</span> java.util.Collections;
<span class="hljs-keyword">import</span> java.util.List;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CustomClassSortingV2</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>{
        List&lt;PersonV2&gt; people = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;(Arrays.asList(
                <span class="hljs-keyword">new</span> PersonV2(<span class="hljs-string">"Alice"</span>, <span class="hljs-number">30</span>, <span class="hljs-number">65.5</span>),
                <span class="hljs-keyword">new</span> PersonV2(<span class="hljs-string">"Bob"</span>, <span class="hljs-number">25</span>, <span class="hljs-number">75.0</span>),
                <span class="hljs-keyword">new</span> PersonV2(<span class="hljs-string">"Charlie"</span>, <span class="hljs-number">35</span>, <span class="hljs-number">80.0</span>)
        ));
        System.out.println(<span class="hljs-string">"Original people list: "</span> + people);

        Collections.sort(people);
        System.out.println(<span class="hljs-string">"Sorted people list: "</span> + people);
    }
}
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">Original people list: [PersonV2 [name=Alice, age=30, weight=65.5 kgs], PersonV2 [name=Bob, age=25, weight=75.0 kgs], PersonV2 [name=Charlie, age=35, weight=80.0 kgs]]
Sorted people list: [PersonV2 [name=Bob, age=25, weight=75.0 kgs], PersonV2 [name=Alice, age=30, weight=65.5 kgs], PersonV2 [name=Charlie, age=35, weight=80.0 kgs]]
</code></pre>
<p>In this example, the <code>PersonV2</code> objects are sorted in ascending order of age using the <code>Collections.sort()</code> method, which relies on the natural ordering defined by the <code>compareTo()</code> method in the <code>PersonV2</code> class.</p>
<h3 id="heading-limitations-of-comparable">Limitations of Comparable</h3>
<p>While the <code>Comparable</code> interface provides a way to define a natural ordering for objects, it has several limitations that can restrict its use in practical applications. Understanding these limitations can help us determine when to use other mechanisms, such as the <code>Comparator</code> interface, to achieve more flexible sorting.</p>
<ul>
<li><strong>Single Natural Ordering</strong>: The primary limitation of <code>Comparable</code> is that it allows only one natural ordering for the objects of a class. When you implement <code>Comparable</code>, you define a single way to compare objects, which is used whenever the objects are sorted or compared. This can be restrictive if you need to sort objects in multiple ways.</li>
<li><strong>Inflexibility</strong>: If you need to sort objects by different attributes or in different orders, you will have to modify the class or create new implementations of <code>Comparable</code>. This inflexibility can lead to a proliferation of comparison methods and can make the code harder to maintain.</li>
<li><strong>Non-Adaptable</strong>: Once a class implements <code>Comparable</code>, the natural ordering is fixed and cannot be easily changed. For instance, if your <code>PersonV2</code> class initially sorts by age but later you need to sort by weight or name, you have to either change the <code>compareTo()</code> method or create a new version of the class.</li>
</ul>
<p>This is where the <code>Comparator</code> interface comes into play. To define multiple ways of comparing objects, we can use the <code>Comparator</code> interface, which we will explore in the next section.</p>
<h2 id="heading-how-to-use-the-comparator-interface">How to Use the Comparator Interface</h2>
<p>The <code>Comparator</code> interface in Java provides a way to define multiple ways of comparing and sorting objects. Unlike the <code>Comparable</code> interface, which allows only a single natural ordering, <code>Comparator</code> is designed to offer flexibility by allowing multiple sorting strategies. This makes it particularly useful for scenarios where objects need to be sorted in different ways.</p>
<h3 id="heading-overview-1">Overview</h3>
<p>The <code>Comparator</code> interface defines a single method, <code>compare()</code>, which compares two objects and returns:</p>
<ul>
<li>A negative integer if the first object is less than the second object.</li>
<li>Zero if the first object is equal to the second object.</li>
<li>A positive integer if the first object is greater than the second object.</li>
</ul>
<p>This method provides a way to define custom ordering for objects without modifying the class itself.</p>
<h3 id="heading-how-comparator-allows-for-multiple-ways-of-ordering-objects">How Comparator Allows for Multiple Ways of Ordering Objects</h3>
<p>The <code>Comparator</code> interface allows you to create multiple <code>Comparator</code> instances, each defining a different ordering for objects. This flexibility means that you can sort objects by various attributes or in different orders without altering the object's class.</p>
<p>Let's implement multiple <code>Comparator</code> instances for the <code>Person</code> class. We'll define comparators for sorting by name, by age, and by weight. First, we need to update the <code>Person</code> class to include getters and ensure that attributes are accessible.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> tutorial;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Person</span> </span>{
    String name;
    <span class="hljs-keyword">int</span> age;
    <span class="hljs-keyword">double</span> weight;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Person</span><span class="hljs-params">(String name, <span class="hljs-keyword">int</span> age, <span class="hljs-keyword">double</span> weight)</span> </span>{
        <span class="hljs-keyword">this</span>.name = name;
        <span class="hljs-keyword">this</span>.age = age;
        <span class="hljs-keyword">this</span>.weight = weight;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getName</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> name;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getAge</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> age;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">getWeight</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> weight;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Person [name="</span> + name + <span class="hljs-string">", age="</span> + age + <span class="hljs-string">", weight="</span> + weight + <span class="hljs-string">" kgs]"</span>;
    }
}
</code></pre>
<h4 id="heading-comparator-by-name"><strong>Comparator by Name</strong></h4>
<p>This comparator sorts <code>Person</code> objects alphabetically by their <code>name</code>.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> tutorial.comparator;

<span class="hljs-keyword">import</span> tutorial.Person;

<span class="hljs-keyword">import</span> java.util.Comparator;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PersonNameComparator</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Comparator</span>&lt;<span class="hljs-title">Person</span>&gt; </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">compare</span><span class="hljs-params">(Person p1, Person p2)</span> </span>{
        <span class="hljs-keyword">return</span> p1.getName().compareTo(p2.getName());
    }
}
</code></pre>
<h4 id="heading-comparator-by-age">Comparator by Age</h4>
<p>This comparator sorts <code>Person</code> objects by their <code>age</code>, in ascending order.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> tutorial.comparator;

<span class="hljs-keyword">import</span> tutorial.Person;

<span class="hljs-keyword">import</span> java.util.Comparator;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PersonAgeComparator</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Comparator</span>&lt;<span class="hljs-title">Person</span>&gt; </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">compare</span><span class="hljs-params">(Person p1, Person p2)</span> </span>{
        <span class="hljs-keyword">return</span> p1.getAge() - p2.getAge();
    }
}
</code></pre>
<h4 id="heading-comparator-by-weight">Comparator by Weight</h4>
<p>This comparator sorts <code>Person</code> objects by their <code>weight</code>, in ascending order.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> tutorial.comparator;

<span class="hljs-keyword">import</span> tutorial.Person;

<span class="hljs-keyword">import</span> java.util.Comparator;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PersonWeightComparator</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Comparator</span>&lt;<span class="hljs-title">Person</span>&gt; </span>{

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">compare</span><span class="hljs-params">(Person p1, Person p2)</span> </span>{
        <span class="hljs-keyword">return</span> (<span class="hljs-keyword">int</span>) (p1.getWeight() - p2.getWeight());
    }
}
</code></pre>
<p>Now, here’s how you can use these <code>Comparator</code> instances to sort a list of <code>Person</code> objects:</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> tutorial;

<span class="hljs-keyword">import</span> tutorial.comparator.PersonAgeComparator;
<span class="hljs-keyword">import</span> tutorial.comparator.PersonNameComparator;
<span class="hljs-keyword">import</span> tutorial.comparator.PersonWeightComparator;

<span class="hljs-keyword">import</span> java.util.ArrayList;
<span class="hljs-keyword">import</span> java.util.Arrays;
<span class="hljs-keyword">import</span> java.util.Collections;
<span class="hljs-keyword">import</span> java.util.List;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CustomClassSortingV3</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>{
        List&lt;Person&gt; people = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;(Arrays.asList(
                <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"Alice"</span>, <span class="hljs-number">30</span>, <span class="hljs-number">65.5</span>),
                <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"Bob"</span>, <span class="hljs-number">25</span>, <span class="hljs-number">75.0</span>),
                <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"Charlie"</span>, <span class="hljs-number">35</span>, <span class="hljs-number">80.0</span>)
        ));
        System.out.println(<span class="hljs-string">"Original people list: "</span> + people);

        Collections.sort(people, <span class="hljs-keyword">new</span> PersonNameComparator());
        System.out.println(<span class="hljs-string">"Sorted people list by name: "</span> + people);

        Collections.sort(people, <span class="hljs-keyword">new</span> PersonAgeComparator());
        System.out.println(<span class="hljs-string">"Sorted people list by age: "</span> + people);

        Collections.sort(people, <span class="hljs-keyword">new</span> PersonWeightComparator());
        System.out.println(<span class="hljs-string">"Sorted people list by weight: "</span> + people);
    }
}
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">Original people list: [Person [name=Alice, age=30, weight=65.5 kgs], Person [name=Bob, age=25, weight=75.0 kgs], Person [name=Charlie, age=35, weight=80.0 kgs]]
Sorted people list by name: [Person [name=Alice, age=30, weight=65.5 kgs], Person [name=Bob, age=25, weight=75.0 kgs], Person [name=Charlie, age=35, weight=80.0 kgs]]
Sorted people list by age: [Person [name=Bob, age=25, weight=75.0 kgs], Person [name=Alice, age=30, weight=65.5 kgs], Person [name=Charlie, age=35, weight=80.0 kgs]]
Sorted people list by weight: [Person [name=Alice, age=30, weight=65.5 kgs], Person [name=Bob, age=25, weight=75.0 kgs], Person [name=Charlie, age=35, weight=80.0 kgs]]
</code></pre>
<p>In this example, the <code>Comparator</code> instances allow sorting the <code>Person</code> objects by different attributes: name, age, and weight. This demonstrates how the <code>Comparator</code> interface enables flexible and versatile sorting strategies for a class.</p>
<h2 id="heading-comparable-vs-comparator">Comparable vs Comparator</h2>
<p>When sorting objects in Java, you have two primary options: the <code>Comparable</code> and <code>Comparator</code> interfaces. Understanding the differences between these two interfaces can help you choose the right approach for your needs. Please note that this is also a very important interview question.</p>
<h3 id="heading-comparison">Comparison</h3>
<p>Here’s a table comparing and contrasting the <code>Comparable</code> and <code>Comparator</code> interfaces in Java:</p>
<table><tbody><tr><th><p>Feature</p></th><th><p>Comparable</p></th><th><p>Comparator</p></th></tr><tr><td><p>Definition</p></td><td><p>Provides a single, natural ordering for objects</p></td><td><p>Provides multiple ways to compare objects</p></td></tr><tr><td><p>Method</p></td><td><p>compareTo(T o)</p></td><td><p>compare(T o1, T o2)</p></td></tr><tr><td><p>Implementation</p></td><td><p>Implemented within the class itself</p></td><td><p>Implemented outside the class</p></td></tr><tr><td><p>Sorting Criteria</p></td><td><p>One default natural ordering</p></td><td><p>Multiple sorting criteria</p></td></tr><tr><td><p>Flexibility</p></td><td><p>Limited to one way of comparing objects</p></td><td><p>Flexible; multiple comparators can be defined</p></td></tr><tr><td><p>Class Modification</p></td><td><p>Requires modifying the class to implement <code>Comparable</code></p></td><td><p>Does not require modifying the class</p></td></tr><tr><td><p>Use Case</p></td><td><p>Use when there is a clear, natural ordering (e.g., sorting employees by ID)</p></td><td><p>Use when different sorting orders are needed or when you cannot modify the class</p></td></tr></tbody></table>

<h3 id="heading-benefits-and-drawbacks-of-each-approach">Benefits and Drawbacks of Each Approach</h3>
<h4 id="heading-comparable-operator">Comparable Operator</h4>
<h5 id="heading-benefits">Benefits:</h5>
<ul>
<li><strong>Simplicity</strong>: Provides a default sorting order that is easy to implement and use.</li>
<li><strong>Built-in</strong>: The natural ordering is part of the class itself, so it is always available and used by default in sorting methods.</li>
</ul>
<h4 id="heading-drawbacks">Drawbacks:</h4>
<ul>
<li><strong>Single Ordering</strong>: Can only define one way to compare objects. If different sorting orders are needed, the class must be modified or additional <code>Comparator</code> instances must be used.</li>
<li><strong>Class Modification</strong>: Requires altering the class to implement <code>Comparable</code>, which might not be feasible if the class is part of a library or if its natural ordering is not clear.</li>
</ul>
<h4 id="heading-comparator">Comparator</h4>
<h5 id="heading-benefits-1">Benefits:</h5>
<ul>
<li><strong>Flexibility</strong>: Allows for multiple sorting orders and criteria, which can be defined externally and used as needed.</li>
<li><strong>Non-invasive</strong>: Does not require modification of the class itself, making it suitable for classes you do not control or when you need different sorting options.</li>
</ul>
<h5 id="heading-drawbacks-1">Drawbacks:</h5>
<ul>
<li><strong>Complexity</strong>: Requires creating and managing multiple <code>Comparator</code> instances, which can add complexity to the code.</li>
<li><strong>Overhead</strong>: Might introduce additional overhead if many comparators are used, especially if they are created on the fly.</li>
</ul>
<p>In summary, <code>Comparable</code> is best used when a class has a natural ordering that makes sense for most use cases. </p>
<p><code>Comparator</code>, on the other hand, provides flexibility for sorting by multiple criteria and is useful when the class does not have a natural ordering or when different sorting orders are needed. </p>
<p>Choosing between <code>Comparable</code> and <code>Comparator</code> depends on your specific sorting needs and whether you need a single default order or multiple flexible sorting options.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Understanding and utilizing both <code>Comparable</code> and <code>Comparator</code> can significantly enhance your ability to manage and manipulate object collections in Java. By applying these concepts, you can create more flexible and powerful sorting mechanisms.</p>
<p>To solidify your understanding, try implementing both <code>Comparable</code> and <code>Comparator</code> in real-world scenarios. Experiment with different classes and sorting criteria to see how each approach works in practice.</p>
<h3 id="heading-links-to-official-java-documentation">Links to Official Java Documentation:</h3>
<ul>
<li><a target="_blank" href="https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html">Java Comparable Interface</a></li>
<li><a target="_blank" href="https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html">Java Comparator Interface</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How To Implement Instant Search with Flask and HTMX ]]>
                </title>
                <description>
                    <![CDATA[ Instant search is a feature that shows search results as users type their query. Instead of waiting for a full page reload or submitting a form, results appear instantly, allowing users to find what they are looking for quickly. For example, when you... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-implement-instant-search-with-flask-and-htmx/</link>
                <guid isPermaLink="false">66ba0e96102ebf67c0a6d42a</guid>
                
                    <category>
                        <![CDATA[ Flask Framework ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Mon, 22 Jul 2024 11:36:53 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/07/instant-search.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Instant search is a feature that shows search results as users type their query. Instead of waiting for a full page reload or submitting a form, results appear instantly, allowing users to find what they are looking for quickly. For example, when you start typing in a search box, suggestions or matching items will appear immediately, making the process smoother and more efficient.</p>
<p>In this tutorial, you'll learn how to create a simple instant search feature using Flask and HTMX. This will help you build interactive web applications with better user experience.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ol>
<li><a class="post-section-overview" href="#heading-how-to-set-up-the-environment">How to Set Up the Environment</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-the-database">How to Set up the Database</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-basic-routing-and-html">How to Set Up Basic Routing and HTML</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-htmx-for-instant-search">How to Add HTMX for Instant Search</a></li>
<li><a class="post-section-overview" href="#heading-demo">Demo</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h3 id="heading-why-use-instant-search"><strong>Why Use Instant Search?</strong></h3>
<ul>
<li><strong>Speed</strong>: Users get immediate feedback, which helps them refine their search.</li>
<li><strong>Convenience</strong>: It reduces the number of clicks and page loads, leading to a more seamless experience.</li>
<li><strong>Engagement</strong>: Users are more likely to stay on your site if they can find what they need easily.</li>
</ul>
<h3 id="heading-technologies-used"><strong>Technologies Used</strong></h3>
<p>To implement this instant search feature, we'll use two main technologies:</p>
<ul>
<li><strong>Flask</strong>: <a target="_blank" href="https://blog.ashutoshkrris.in/getting-started-with-flask">Flask</a> is a popular web framework for Python. It is simple and lightweight, making it easy to set up and start building web applications quickly. Flask lets you to create routes, handle requests, and serve HTML templates with minimal setup.</li>
<li><strong>HTMX</strong>: This is a powerful JavaScript library that lets you to create dynamic web pages without having to write a lot of JavaScript code. With HTMX, you can update parts of a page based on user actions, like typing in a search box. It makes it easy to load data from the server and display it on the page without a full reload.</li>
</ul>
<h2 id="heading-how-to-set-up-the-environment">How to Set Up the Environment</h2>
<p>In this section, we'll set up the environment for our Flask project, including installing the necessary packages and organizing the project structure.</p>
<h4 id="heading-1-how-to-install-flask-and-htmx">1. How to Install Flask and HTMX</h4>
<p>First, you need to install Flask, Flask-SQLAlchemy, and Flask-Migrate. You can do this using pip. Open your terminal and run:</p>
<pre><code class="lang-bash">pip install Flask Flask-SQLAlchemy Flask-Migrate
</code></pre>
<p>For HTMX, we'll include it in our HTML template directly from a CDN.</p>
<h4 id="heading-2-how-to-create-a-virtual-environment">2. How to Create a Virtual Environment</h4>
<p>It's a good practice to create a virtual environment for your projects to manage dependencies. Here's how to create one:</p>
<pre><code class="lang-bash">python -m venv venv
</code></pre>
<p>Next, activate the environment:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># On Windows</span>
venv\Scripts\activate

<span class="hljs-comment"># On macOS/Linux</span>
<span class="hljs-built_in">source</span> venv/bin/activate
</code></pre>
<h4 id="heading-3-how-to-set-up-the-project-structure">3. How to Set Up the Project Structure</h4>
<p>Now, set up your project structure as follows:</p>
<pre><code class="lang-bash">my_flask_app/
├── core/
│   ├── __init__.py
│   ├── models.py
│   └── routes.py
├── config.py
└── main.py
</code></pre>
<p>Let us start with creating the first file: <strong>core/<strong>init</strong>.py</strong>. This file is the initialization script for the core module of our Flask application. It sets up the Flask app instance and configures it using the settings from the <code>DevelopmentConfig</code> class, and initializes the database and migration system.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask
<span class="hljs-keyword">from</span> flask_sqlalchemy <span class="hljs-keyword">import</span> SQLAlchemy
<span class="hljs-keyword">from</span> flask_migrate <span class="hljs-keyword">import</span> Migrate
<span class="hljs-keyword">from</span> config <span class="hljs-keyword">import</span> DevelopmentConfig

<span class="hljs-comment"># Create the Flask app instance</span>
app = Flask(__name__)

<span class="hljs-comment"># Load configuration from DevelopmentConfig</span>
app.config.from_object(DevelopmentConfig)

<span class="hljs-comment"># Initialize SQLAlchemy with the app instance</span>
db = SQLAlchemy(app)

<span class="hljs-comment"># Initialize Flask-Migrate with the app instance and database</span>
migrate = Migrate(app, db)

<span class="hljs-comment"># Import routes to register them with the app</span>
<span class="hljs-keyword">from</span> core <span class="hljs-keyword">import</span> routes
</code></pre>
<p>Next, we will create the <strong>config.py</strong> file from where we'll import the <code>DevelopmentConfig</code> class. This file contains configuration settings for different environments (development, testing, production). These settings help manage different behaviors and configurations based on where your app is running.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span>(<span class="hljs-params">object</span>):</span>
    DEBUG = <span class="hljs-literal">False</span>
    TESTING = <span class="hljs-literal">False</span>
    CSRF_ENABLED = <span class="hljs-literal">True</span>
    SECRET_KEY = <span class="hljs-string">"guess-me"</span>
    SQLALCHEMY_DATABASE_URI = <span class="hljs-string">"sqlite:///db.sqlite"</span>
    SQLALCHEMY_TRACK_MODIFICATIONS = <span class="hljs-literal">False</span>
    BCRYPT_LOG_ROUNDS = <span class="hljs-number">13</span>
    WTF_CSRF_ENABLED = <span class="hljs-literal">True</span>
    DEBUG_TB_ENABLED = <span class="hljs-literal">False</span>
    DEBUG_TB_INTERCEPT_REDIRECTS = <span class="hljs-literal">False</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DevelopmentConfig</span>(<span class="hljs-params">Config</span>):</span>
    DEVELOPMENT = <span class="hljs-literal">True</span>
    DEBUG = <span class="hljs-literal">True</span>
    WTF_CSRF_ENABLED = <span class="hljs-literal">False</span>
    DEBUG_TB_ENABLED = <span class="hljs-literal">True</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestingConfig</span>(<span class="hljs-params">Config</span>):</span>
    TESTING = <span class="hljs-literal">True</span>
    DEBUG = <span class="hljs-literal">True</span>
    SQLALCHEMY_DATABASE_URI = <span class="hljs-string">"sqlite:///testdb.sqlite"</span>
    BCRYPT_LOG_ROUNDS = <span class="hljs-number">1</span>
    WTF_CSRF_ENABLED = <span class="hljs-literal">False</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProductionConfig</span>(<span class="hljs-params">Config</span>):</span>
    DEBUG = <span class="hljs-literal">False</span>
    DEBUG_TB_ENABLED = <span class="hljs-literal">False</span>
</code></pre>
<ul>
<li><code>Config</code>: The base configuration class with default settings.</li>
<li><code>DevelopmentConfig</code>: Inherits from <code>Config</code> and overrides development settings.</li>
<li><code>TestingConfig</code>: Inherits from <code>Config</code> and overrides settings for testing.</li>
<li><code>ProductionConfig</code>: Inherits from <code>Config</code> and overrides production settings.</li>
</ul>
<p>Finally, we'll create the <strong>main.py</strong> file. This is the entry point of our application. When we run this file, it starts the Flask web server.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> core <span class="hljs-keyword">import</span> app

<span class="hljs-comment"># Start the Flask app</span>
<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    app.run(debug=<span class="hljs-literal">True</span>)
</code></pre>
<ul>
<li><code>if __name__ == '__main__'</code>: This ensures that the Flask app runs only if the script is executed directly (not imported as a module).</li>
<li><code>app.run(debug=True)</code>: Starts the Flask development server with debug mode enabled, which provides detailed error messages and auto-reloading.</li>
</ul>
<p>Now that you understand the project files, we can proceed with implementing the instant search functionality. This will involve creating the models and search route, setting up the HTMX-powered front-end, and connecting everything to fetch and display search results dynamically.</p>
<h2 id="heading-how-to-set-up-the-database">How to Set up the Database</h2>
<p>In this section, we will set up the database for our Flask application. We will use SQLite for simplicity. We will create a model for the data we want to search and seed the database with sample data.</p>
<p>SQLite is a lightweight, disk-based database that doesn’t require a separate server process. It's an excellent choice for development and small projects because it is easy to set up and use.</p>
<h3 id="heading-how-to-create-a-model-for-the-data-to-be-searched">How to Create a Model for the Data to Be Searched</h3>
<p>We will create a <code>Book</code> model to represent the data in our database. This model will include fields like the book title and author.</p>
<p>Let's create the <code>core/models.py</code> file and add the model there:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> core <span class="hljs-keyword">import</span> db

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Book</span>(<span class="hljs-params">db.Model</span>):</span>
    id = db.Column(db.Integer, primary_key=<span class="hljs-literal">True</span>)
    title = db.Column(db.String(<span class="hljs-number">100</span>), nullable=<span class="hljs-literal">False</span>)
    author = db.Column(db.String(<span class="hljs-number">100</span>), nullable=<span class="hljs-literal">False</span>)
</code></pre>
<h3 id="heading-how-to-apply-migrations-using-flask-migrate">How to Apply Migrations Using Flask-Migrate</h3>
<p>Before we can seed our database, we need to set up database migrations using Flask-Migrate. This tool helps us manage database changes, such as creating tables and altering schemas, systematically.</p>
<p>Initialize the migrations folder by running the following command in your project directory:</p>
<pre><code class="lang-bash">flask db init
</code></pre>
<p>This command creates a <strong>migrations</strong> directory in our project, which will store migration scripts.</p>
<p>Generate a migration script that creates the necessary database tables based on your models:</p>
<pre><code class="lang-bash">flask db migrate -m <span class="hljs-string">"Initial migration"</span>
</code></pre>
<p>This command scans your models and generates a new migration script in the <strong>migrations</strong> folder.</p>
<p>Apply the migration to create the tables in your database:</p>
<pre><code class="lang-bash">flask db upgrade
</code></pre>
<p>This command executes the migration script, creating the tables defined by your models in the database. Post this step, you will see an <strong>instance/db.sqlite</strong> file created.</p>
<h3 id="heading-how-to-seed-data-into-your-database">How to Seed Data Into Your Database</h3>
<p>Now that we have set up the database and applied the migration, we can proceed with seeding the database. Create a file named <strong>seeder.py</strong> with the following content:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> csv
<span class="hljs-keyword">from</span> sqlalchemy.exc <span class="hljs-keyword">import</span> IntegrityError

<span class="hljs-keyword">from</span> core <span class="hljs-keyword">import</span> db, app
<span class="hljs-keyword">from</span> core.models <span class="hljs-keyword">import</span> Book


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">seed_data</span>():</span>
    <span class="hljs-keyword">with</span> app.app_context():
        <span class="hljs-comment"># Open the CSV file</span>
        <span class="hljs-keyword">with</span> open(<span class="hljs-string">"data.csv"</span>, newline=<span class="hljs-string">''</span>, encoding=<span class="hljs-string">'utf-8'</span>) <span class="hljs-keyword">as</span> csvfile:
            reader = csv.DictReader(csvfile)

            <span class="hljs-comment"># Iterate over the rows in the CSV file</span>
            <span class="hljs-keyword">for</span> row <span class="hljs-keyword">in</span> reader:
                <span class="hljs-comment"># Create a new Book instance</span>
                book = Book(
                    title=row[<span class="hljs-string">'Book Name'</span>],
                    author=row[<span class="hljs-string">'Author Name'</span>]
                )

                <span class="hljs-comment"># Add the book to the session</span>
                db.session.add(book)

            <span class="hljs-keyword">try</span>:
                <span class="hljs-comment"># Commit the session to write the books to the database</span>
                db.session.commit()
                print(<span class="hljs-string">"Books added successfully."</span>)
            <span class="hljs-keyword">except</span> IntegrityError <span class="hljs-keyword">as</span> e:
                db.session.rollback()
                print(<span class="hljs-string">f"Error occurred: <span class="hljs-subst">{e}</span>"</span>)


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    seed_data()
</code></pre>
<p>The seeder script is responsible for populating the database with initial data. This is useful for testing and development purposes, allowing you to work with a set of sample data. This script reads data from <strong>data.csv</strong>, and processes it to insert it into the database.</p>
<p><strong>Note</strong>: You can download the <a target="_blank" href="https://github.com/ashutoshkrris/instant-search-with-flask-htmx/blob/main/data.csv">data.csv</a> file from here.</p>
<p>To use this script, ensure your <strong>data.csv</strong> file exists in the same directory as <strong>seeder.py</strong>. Run the script using Python:</p>
<pre><code class="lang-bash">python seeder.py
</code></pre>
<h2 id="heading-how-to-set-up-basic-routing-and-html">How to Set Up Basic Routing and HTML</h2>
<p>In this section, we'll set up a basic route in Flask to serve an index page (<strong>index.html</strong>) where users can search and display books.</p>
<h3 id="heading-how-to-set-up-flask-route">How to Set Up Flask Route</h3>
<p>Let's set up a Flask route (<code>/</code>) to render an <strong>index.html</strong> template and display books. For that, create a <strong>core/routes.py</strong> file and add the following route:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> render_template
<span class="hljs-keyword">from</span> core <span class="hljs-keyword">import</span> app
<span class="hljs-keyword">from</span> core.models <span class="hljs-keyword">import</span> Book

<span class="hljs-meta">@app.route('/')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>():</span>
    <span class="hljs-comment"># Fetch the first 20 books to display by default</span>
    books = Book.query.limit(<span class="hljs-number">20</span>).all()
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">"index.html"</span>, books=books)
</code></pre>
<p>The Flask application handles routing through the <code>@app.route('/')</code> decorator, which directs requests to the root URL (<code>/</code>). When a user visits the homepage, the <code>index()</code> function is invoked. </p>
<p>Inside this function, we query the <code>Book</code> model using SQLAlchemy to fetch the first 20 books from the database. These books are then passed as a parameter (<code>books</code>) to the <code>render_template</code> function, which renders the <strong>index.html</strong> template.</p>
<h3 id="heading-how-to-creating-the-indexhtml-template">How to Creating the index.html Template</h3>
<p>Create a file named <strong>index.html</strong> inside a <strong>templates</strong> directory in your project. The <strong>templates</strong> directory will lie in the <code>core</code> package. This file will contain the HTML structure for our book search page.</p>
<pre><code class="lang-xml"><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> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</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">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Book Search<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">"https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.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">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"section"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"columns"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"column is-one-third is-offset-one-third"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input"</span> <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Search"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"query"</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">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"table is-fullwidth"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">thead</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>ID<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Book Title<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Book Author<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">thead</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"results"</span>&gt;</span>
        {% for book in books %}
        <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ book.id }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ book.title }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ book.author }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
        {% endfor %}
      <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">section</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>This HTML file uses the Bulma CSS framework for styling and includes elements such as an input field for user searches and a table to display book details fetched from the database.</p>
<p>The <code>index.html</code> template utilizes Jinja2 templating to dynamically populate the table rows (<code>&lt;tr&gt;</code>) with book data retrieved from the Flask backend. Each book's <code>ID</code>, <code>title</code>, and <code>author</code> are displayed in the table rows using <code>{{</code><a target="_blank" href="http://book.id"><code>book.id</code></a><code>}}</code>, <code>{{ book.title }}</code>, and <code>{{</code><a target="_blank" href="http://book.author"><code>book.author</code></a><code>}}</code> respectively.</p>
<h3 id="heading-how-to-run-the-application">How to Run the Application</h3>
<p>Let's run the application using the following command:</p>
<pre><code class="lang-bash">flask run
</code></pre>
<p>Once your application is up and running, this what how it should look like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1721316465728/d5037fd3-f49b-4c59-993c-4ff7c23cabef.png" alt="Book Search Application Home Page" width="1918" height="937" loading="lazy">
<em>web page with ID, book titles, and book authors</em></p>
<h2 id="heading-how-to-add-htmx-for-instant-search">How to Add HTMX for Instant Search</h2>
<p>Finally, we'll add HTMX to enhance our Flask application with dynamic search capabilities. For this, we'll introduce a new route and modify existing HTML template.</p>
<h3 id="heading-how-to-create-the-search-route">How to Create the Search Route</h3>
<p>First, create a new route <code>/search</code> in your Flask application to handle book searches based on user input:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> render_template, request
<span class="hljs-keyword">from</span> core <span class="hljs-keyword">import</span> app
<span class="hljs-keyword">from</span> core.models <span class="hljs-keyword">import</span> Book

<span class="hljs-meta">@app.route('/search')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">search</span>():</span>
    query = request.args.get(<span class="hljs-string">"query"</span>)
    <span class="hljs-keyword">if</span> query:
        results = Book.query.filter(Book.title.ilike(<span class="hljs-string">f"%<span class="hljs-subst">{query}</span>%"</span>) | Book.author.ilike(<span class="hljs-string">f"%<span class="hljs-subst">{query}</span>%"</span>)).limit(<span class="hljs-number">10</span>).all()
    <span class="hljs-keyword">else</span>:
        results = Book.query.limit(<span class="hljs-number">20</span>).all()
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">"search_results.html"</span>, results=results)
</code></pre>
<p>This route listens for <code>GET</code> requests to <code>/search</code>. It retrieves the search query from the URL parameter using <code>request.args.get("query")</code>. </p>
<p>If a <code>query</code> parameter is present, it uses SQLAlchemy's <code>ilike</code> method to perform a case-insensitive search across the <code>title</code> and <code>author</code> columns of the <code>Book</code> table, fetching up to 10 results.</p>
<p>If no query parameter is provided, it defaults to fetching the first 20 books from the database. The results are passed to a new <code>search_results.html</code> template for rendering.</p>
<h3 id="heading-how-to-modify-indexhtml-to-add-htmx">How to Modify index.html to Add HTMX</h3>
<pre><code class="lang-xml"><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> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</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">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span> /&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Book Search<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">"https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css"</span> /&gt;</span>
  <span class="hljs-comment">&lt;!-- Include HTMX library --&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/htmx.org/dist/htmx.min.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">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">section</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"section"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"columns"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"column is-one-third is-offset-one-third"</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- HTMX-enabled search input --&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">class</span>=<span class="hljs-string">"input"</span>
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Search"</span>
            <span class="hljs-attr">name</span>=<span class="hljs-string">"query"</span>
            <span class="hljs-attr">hx-get</span>=<span class="hljs-string">"/search"</span>
            <span class="hljs-attr">hx-trigger</span>=<span class="hljs-string">"keyup changed delay:500ms"</span>
            <span class="hljs-attr">hx-target</span>=<span class="hljs-string">"#results"</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">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"table is-fullwidth"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">thead</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>ID<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Book Title<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">th</span>&gt;</span>Book Author<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">thead</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"results"</span>&gt;</span>
        {% for book in books %}
          <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ book.id }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ book.title }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ book.author }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
        {% endfor %}
      <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">section</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>The <code>&lt;script&gt;</code> tag imports the HTMX library from a CDN, enabling client-side interactions without requiring complex JavaScript. In addition to that, we enhanced the <code>&lt;input&gt;</code> element with HTMX attributes:</p>
<ul>
<li><code>hx-get="/search"</code>: Specifies the endpoint (<code>/search</code>) to send GET requests when the user types in the input field.</li>
<li><code>hx-trigger="keyup changed delay:500ms"</code>: Triggers the search action after a 500ms delay when the user types (<code>keyup</code>) or changes the input (<code>changed</code>).</li>
<li><code>hx-target="#results"</code>: Updates the content of the element with <code>id="results"</code> with the response from the <code>/search</code> endpoint.</li>
</ul>
<h3 id="heading-how-to-create-the-searchresultshtml-template">How to Create the search_results.html Template</h3>
<p>Next, we will create a new template <strong>search_results.html</strong> to display search results:</p>
<pre><code class="lang-xml">{% for result in results %}
<span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ result.id }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ result.title }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span>{{ result.author }}<span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
{% endfor %}
</code></pre>
<p>This template iterates over <code>results</code>, which are passed from the <code>/search</code> route. For each book in <code>results</code>, generates a table row (<code>&lt;tr&gt;</code>) that displays the book's ID, title, and author.</p>
<h2 id="heading-demo">Demo</h2>
<p>Finally, we have implemented instant search with HTMX in our Flask application. Here's what our final application should look like:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/llCmZXaopX0" 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>You'd notice a delay in the search results. This is called debouncing. It is a technique used in programming and web development to limit the rate at which a function or event handler is executed. It ensures that a function is only executed after a certain amount of time has passed since the last invocation of the function. </p>
<p>In our case, we set the delay to 500ms before it calls the <code>/search</code> API again. This ensures that we do not hit the API for every character the user types.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you learned how to implement instant search using Flask and HTMX, focusing on enhancing user interaction and performance. By integrating HTMX for AJAX interactions, we enabled dynamic updates to search results without refreshing the entire page. </p>
<p>This approach not only improves user experience by providing real-time feedback but also optimizes server load by debouncing search queries. </p>
<p>By mastering these techniques, you're equipped to build responsive web applications that deliver seamless search experiences, combining the flexibility of Flask with the interactivity of HTMX to meet diverse user needs efficiently and effectively.</p>
<p>You can find the code for this tutorial in this repository: <a target="_blank" href="https://github.com/ashutoshkrris/instant-search-with-flask-htmx">https://github.com/ashutoshkrris/instant-search-with-flask-htmx</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Set Up Magic Link Authentication with React, Flask, and Authsignal ]]>
                </title>
                <description>
                    <![CDATA[ Authentication is the process of verifying the identity of a user or system. It ensures that only authorized individuals or systems can access certain resources or perform specific actions. Magic Link Authentication offers a simple yet secure way for... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/set-up-magic-link-authentication-with-react-flask-and-authsignal/</link>
                <guid isPermaLink="false">66ba0ec19065919bb4e84ca8</guid>
                
                    <category>
                        <![CDATA[ authentication ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flask Framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Thu, 11 Jan 2024 18:40:41 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/01/magic-link-authsignal.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Authentication is the process of verifying the identity of a user or system. It ensures that only authorized individuals or systems can access certain resources or perform specific actions.</p>
<p>Magic Link Authentication offers a simple yet secure way for users to log in without passwords. This tutorial will walk you through the implementation of Magic Link Authentication using React for the front end, Flask for the back end, and the authentication service provided by <a target="_blank" href="https://www.authsignal.com/">Authsignal</a>.</p>
<h3 id="heading-table-of-contents">Table of Contents:</h3>
<ol>
<li><a class="post-section-overview" href="#heading-understanding-magic-link-authentication">Understanding Magic Link Authentication</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/set-up-magic-link-authentication-with-react-flask-and-authsignal/how-to-configure-authsignal">How to Configure Authsignal</a></li>
<li><a class="post-section-overview" href="#heading-application-flow">Application Flow</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-your-backend-server">How to Set Up Your Backend Server</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-environment-variables">How to Set Up Environment Variables</a></li>
<li><a class="post-section-overview" href="#heading-how-to-initialize-the-authsignal-client">How to Initialize the Authsignal Client</a></li>
<li><a class="post-section-overview" href="#heading-authsignal-actions">Authsignal Actions</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-the-required-routes">How to Create the Required Routes</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-a-new-frontend-react-project">How to Set Up a New Frontend React Project</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-the-components">How to Set Up the Components</a></li>
<li><a class="post-section-overview" href="#heading-how-to-run-the-application">How to Run the Application</a></li>
<li><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></li>
</ol>
<h2 id="heading-understanding-magic-link-authentication">Understanding Magic Link Authentication</h2>
<p>Magic link authentication is a convenient and secure authentication method that simplifies users' login process. Instead of entering a username and password, users receive a unique link via email. This link, known as a magic link, grants them access to their account without traditional credentials.</p>
<p>One key difference between magic link authentication and authentication with email verification is the user experience. With magic link authentication, users can authenticate with just a single click. They don't need to remember or enter a password, which can be especially beneficial for users who struggle with password management or find it inconvenient to type in their credentials repeatedly.</p>
<p>While email verification adds an extra layer of security, it may require the user to remember additional credentials or go through multiple steps before accessing their account. If you're interested in learning more about email verification, you can check out my article <a target="_blank" href="https://blog.ashutoshkrris.in/how-to-set-up-email-verification-in-a-flask-app">here</a> to dive deeper into the topic.</p>
<h2 id="heading-how-to-configure-authsignal">How to Configure Authsignal</h2>
<p><a target="_blank" href="https://www.authsignal.com/">Authsignal</a> is a service that makes implementing modern authentication methods (like Magic Links and Passkeys) easier. It provides simple tools to integrate secure login methods into your web apps without hassle.</p>
<p>Before proceeding with the tutorial, you need to create an Authsignal account. To do that, you can follow these steps:</p>
<p>First, go to <a target="_blank" href="https://portal.authsignal.com/users/sign_up">authsignal.com</a> and click on "Create Free Account".</p>
<p>In the next step, create your first tenant. Choose any name for your tenant and select the data storage region.</p>
<p><img src="https://lh7-us.googleusercontent.com/PoQ1Jl8b1fNXmzruv750erSeyi4jxnVlI_QAvoHDH6-O6GVHmDQ07yd2U7WxHrYTUMCyKowll7W-Bs0dBuet9KqiF-mZuV_w8IbFO5tpYziI5M5kaO1ipWEaJPJ7dkPWTNtXyib-BE-8S5VcVtanNNc" alt="Image" width="1600" height="795" loading="lazy">
<em>Creating Tenant on Authsignal</em></p>
<p>Next, you need to configure the authenticators you want to use for your application. For example, I have enabled Email Magic Link and Authenticator App (TOTP).</p>
<p><img src="https://lh7-us.googleusercontent.com/AagTYGVbXToDeHqe4S-lFUx2qgIerUbzlUnGTv3sxZ2EyPBzfDeXNcvT-_oeQksckyhGFHX2YY6g8heKHdIz18qf2N_ejed9fJDFA_pSMzfKX3d5Tid4eDnrn7PUbEX_zVh10urhFa49Ek-eSYZJdAA" alt="Image" width="1435" height="952" loading="lazy">
<em>Configuring Authenticators</em></p>
<p>Once you have configured the authenticator, navigate to the API Keys option. Here, you will find your Secret Key, which will be necessary for implementing the authentication.</p>
<p><img src="https://lh7-us.googleusercontent.com/UJgdqGLl6IRK8sr3NOsf1BXVp7EJpSFMkxTzdRw0QNhz7DqL5fyGMn7KBotMvrp3ivZnYtw8M-fdVX-aJgNrdszRyAziVCxIAXAxb-g8r42F9ZgQFlpm9D1FYicnhuS4DcS5V7hZ430FM5ruEUioiSw" alt="Image" width="926" height="857" loading="lazy">
<em>Finding your secret key</em></p>
<h2 id="heading-application-flow">Application Flow</h2>
<p>Let's understand the flow of the application:</p>
<h3 id="heading-initial-visit">Initial Visit</h3>
<ul>
<li>The user visits the application's user interface. On the user interface, they see a login input box and a signup option. Since the user is new, they opt to sign up.</li>
</ul>
<h3 id="heading-signup-flow">Signup Flow</h3>
<ul>
<li>Upon clicking the signup link, the user is directed to a page to enter their chosen username.</li>
<li>After entering the username and clicking the signup button, the front end triggers a POST API call to /api/signup, sending the username in the request body.</li>
<li>The backend receives the request and communicates with the Authsignal server for user authentication.</li>
<li>Authsignal prompts the user to set up Magic Link authentication by entering their email address.</li>
<li>Authsignal sends a magic link to the provided email address.</li>
<li>After clicking the magic link, the user is authenticated and redirected to the home page, where they receive a welcome message displaying their email address. The page also includes a logout button.</li>
</ul>
<h3 id="heading-login-flow">Login Flow</h3>
<ul>
<li>The user logs out and returns to the login page.</li>
<li>Here, the user enters their registered username and clicks the Login button.</li>
<li>Upon clicking Login, the front end triggers a POST API call to /api/login, passing the username in the request body.</li>
<li>The backend again communicates with Authsignal for user authentication, prompting the setup of Magic Link authentication.</li>
<li>The user is directed to a page to enter their email address.</li>
<li>Authsignal sends a magic link to the provided email address.</li>
<li>After clicking the magic link, the user is authenticated and redirected to the home page, greeted with a welcome message displaying their email address.</li>
</ul>
<p>This flow ensures users can sign up using a chosen username, and set up Magic Link authentication via email for both signup and login. Here is a video tutorial to visually aid you in understanding the flow:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/kr8frW5Wwcg" 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>
<h2 id="heading-how-to-set-up-your-backend-server">How to Set Up Your Backend Server</h2>
<p>In this section, I will guide you through how to set up your Flask server for implementing Magic Link Authentication. Before we begin, it's recommended to set up a virtual environment to isolate your project's dependencies. Here's how you can do it:</p>
<ol>
<li>Open your terminal or command prompt.</li>
<li>Navigate to your project's directory.</li>
<li>Run the following command to create a new virtual environment:</li>
</ol>
<pre><code class="lang-bash">python -m venv myenv
</code></pre>
<p>Note: Replace <code>myenv</code> with the desired name for your virtual environment.</p>
<ol start="4">
<li><p>Activate the virtual environment using the appropriate command for your operating system:</p>
</li>
<li><p>For Windows:</p>
</li>
</ol>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> myenv/Scripts/activate
</code></pre>
<ul>
<li>For macOS/Linux:</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> myenv/bin/activate
</code></pre>
<p>Now that you have your virtual environment set up, let's install the necessary dependencies.</p>
<p>To begin, make sure you have Flask installed, which is a micro web framework for Python. You can install it using the following one-liner:</p>
<pre><code class="lang-bash">pip install Flask
</code></pre>
<p>Next, we need <code>python-decouple</code>, a library that helps manage configuration settings in separate files. Install it with the following command:</p>
<pre><code class="lang-bash">pip install python-decouple
</code></pre>
<p>The <code>flask-cors</code> library is a Flask extension that allows for Cross-Origin Resource Sharing (CORS) support in your Flask application.</p>
<pre><code class="lang-bash">pip install flask-cors
</code></pre>
<p>Finally, we need to install the Python SDK for Authsignal. You can install it with the following command:</p>
<pre><code class="lang-bash">pip install authsignal
</code></pre>
<p>Now that we have all the necessary dependencies installed, let's create a sample Flask server to get started with Magic Link Authentication. Here's a basic setup to help you get started:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask
<span class="hljs-keyword">from</span> flask_cors <span class="hljs-keyword">import</span> CORS

app = Flask(__name__)
CORS(app, supports_credentials=<span class="hljs-literal">True</span>)

<span class="hljs-meta">@app.route("/")</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">hello</span>():</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello, world!"</span>

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    app.run(debug=<span class="hljs-literal">True</span>)
</code></pre>
<p>In the next steps, we will integrate AuthSignal and implement the Magic Link Authentication functionality into this server.</p>
<h2 id="heading-how-to-set-up-environment-variables">How to Set Up Environment Variables</h2>
<p>To successfully configure and integrate AuthSignal into your Flask server, you need to set up the following environment variables:</p>
<ul>
<li><code>AUTHSIGNAL_BASE_URL</code>: This variable contains the base URL of the Authsignal server. It allows your server to communicate with Authsignal's authentication service.</li>
<li><code>AUTHSIGNAL_SECRET_KEY</code>: This variable contains the secret key associated with your Authsignal project. It is used for secure communication between your server and AuthSignal.</li>
<li><code>SECRET_KEY</code>: This variable is a random key used to encrypt the cookies and send them to the browser.</li>
</ul>
<p>Setting environment variables instead of hardcoding in the code provides improved security by keeping sensitive information, such as API keys and secret keys, separate from the codebase. This reduces the risk of accidental exposure or unauthorized access to these credentials.</p>
<p>To set up these environment variables, you can follow these steps:</p>
<ol>
<li>Open a terminal or command prompt.</li>
<li>Navigate to the directory where your Flask server is located.</li>
<li>Create a <code>.env</code> file and export the environment variables using the following commands:</li>
</ol>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> AUTHSIGNAL_BASE_URL=&lt;base_url&gt;
<span class="hljs-built_in">export</span> AUTHSIGNAL_SECRET_KEY=&lt;secret_key&gt;
<span class="hljs-built_in">export</span> SECRET_KEY=&lt;random-secret-key&gt;
</code></pre>
<p>Make sure to replace <code>&lt;base_url&gt;</code>, <code>&lt;secret_key&gt;</code>, and <code>&lt;random-secret-key&gt;</code> with the appropriate values for your Authsignal project. You can find the values for these environment variables in the API Keys section of the Authsignal dashboard as explained earlier.</p>
<p>Note: The method for setting environment variables can vary depending on your operating system. The above commands are applicable for Unix-based systems. For Windows, you can use the <code>set</code> command instead of <code>export</code>.</p>
<p>To export the variables added in the .env file, you can use the following command in the terminal:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> .env
</code></pre>
<p>By properly setting these environment variables, your Flask server can securely communicate with Authsignal and implement the Magic Link Authentication functionality.</p>
<h2 id="heading-how-to-initialize-the-authsignal-client">How to Initialize the Authsignal Client</h2>
<p>To integrate Authsignal into your Flask server and implement Magic Link Authentication, you need to initialize the Authsignal client. Here's an example of how you can do this:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask
<span class="hljs-keyword">from</span> flask_cors <span class="hljs-keyword">import</span> CORS
<span class="hljs-keyword">import</span> authsignal.client
<span class="hljs-keyword">from</span> decouple <span class="hljs-keyword">import</span> config

app = Flask(__name__)
CORS(app)

AUTHSIGNAL_BASE_URL = config(<span class="hljs-string">"AUTHSIGNAL_BASE_URL"</span>)
AUTHSIGNAL_SECRET_KEY = config(<span class="hljs-string">"AUTHSIGNAL_SECRET_KEY"</span>)
SECRET_KEY = config(<span class="hljs-string">"SECRET_KEY"</span>)

authsignal_client = authsignal.Client(
    api_key=AUTHSIGNAL_SECRET_KEY,
    api_url=AUTHSIGNAL_BASE_URL
)
</code></pre>
<p>In this code snippet, we first import the <code>authsignal.client</code>, the Python SDK for Authsignal. We also import config from python-decouple to retrieve the environment variables.</p>
<p>We retrieve the environment variables <code>AUTHSIGNAL_BASE_URL</code>, <code>AUTHSIGNAL_SECRET_KEY</code> and <code>SECRET_KEY</code> using config from python-decouple.</p>
<p>Finally, we initialize the <code>authsignal.Client</code> by passing in the API key <code>AUTHSIGNAL_SECRET_KEY</code> and the base URL of the Authsignal server <code>AUTHSIGNAL_BASE_URL</code>.</p>
<p>By initializing the Authsignal client, we are ready to implement the Magic Link Authentication functionality in our Flask server.</p>
<h2 id="heading-authsignal-actions">Authsignal Actions</h2>
<p>Authsignal allows you to create actions to track and manage user interactions in your application. Actions are events that can be triggered by users, such as signing up or logging in. By creating custom actions, you can have more control over the authentication process and implement specific authentication methods like Magic Link Authentication.</p>
<p>To create an action on your Authsignal dashboard, follow these steps:</p>
<ol>
<li>Click on "<strong>Actions</strong>" in your Authsignal dashboard.</li>
<li>Click on "<strong>Configure a new action</strong>" to create a new action.</li>
<li>Enter a name for the action that describes its purpose or the user interaction it represents.</li>
<li>Next, you can configure the rule for the action. In our case, since we want to implement Magic Link Authentication, we will add a rule to challenge users with Email Magic Link. This will send a magic link to the user's email for authentication.</li>
<li>Save the action to apply the rule and make it active.</li>
</ol>
<p>Here is a video demonstrating the process of creating an Authsignal action and configuring it for Magic Link Authentication:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/01/action.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Creating action on Authsignal</em></p>
<p>You can create two actions – "<strong>signUp</strong>" and "<strong>signIn</strong>". In the next steps, we will make use of these actions.</p>
<h2 id="heading-how-to-create-the-required-routes">How to Create the Required Routes</h2>
<p>Finally, to implement the Magic Link Authentication, we need to create three routes: <code>/api/signup</code>, <code>/api/login</code>, <code>/api/callback</code>, and <code>/api/user</code>.</p>
<h3 id="heading-apisignup-route"><code>/api/signup</code> Route</h3>
<p>The <code>/api/signup</code> route is responsible for allowing the users to register in our application. Here's how we implement it:</p>
<pre><code class="lang-python"><span class="hljs-meta">@app.route('/api/signup', methods=['POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">signup</span>():</span>
    username = request.json.get(<span class="hljs-string">'username'</span>)
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> username:
        <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Missing username parameter'</span>}), <span class="hljs-number">400</span>

    response = authsignal_client.track(
        user_id=username,
        action=<span class="hljs-string">"signUp"</span>,
        payload={
            <span class="hljs-string">"user_id"</span>: username,
            <span class="hljs-string">"redirectUrl"</span>: <span class="hljs-string">"http://localhost:5000/api/callback"</span>
        }
    )
    <span class="hljs-keyword">return</span> jsonify(response), <span class="hljs-number">200</span>
</code></pre>
<p>In this implementation, the route expects a JSON payload containing the <code>username</code> parameter. It then uses the <code>authsignal_client</code> to track the user's <strong>signUp</strong> action and generate a Magic Link. The <code>track</code> method lets you record actions performed by users and initiate challenges. The <code>redirectUrl</code> specifies the URL where the user will be redirected after they have been authenticated. We will create this API next.</p>
<h3 id="heading-apicallback-route"><code>/api/callback</code> Route</h3>
<p>The <code>/api/callback</code> route handles the callback URL where the user is redirected after verifying themselves. Here's the implementation for this route:</p>
<pre><code class="lang-python"><span class="hljs-meta">@app.route('/api/callback', methods=['GET'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">callback</span>():</span>
    token = request.args.get(<span class="hljs-string">'token'</span>)
    challenge_response = authsignal_client.validate_challenge(token)

    <span class="hljs-keyword">if</span> challenge_response[<span class="hljs-string">"state"</span>] == <span class="hljs-string">'CHALLENGE_SUCCEEDED'</span>:
        encoded_token = jwt.encode(
            payload={<span class="hljs-string">"username"</span>: challenge_response[<span class="hljs-string">"user_id"</span>]},
            key=SECRET_KEY,
            algorithm=<span class="hljs-string">"HS256"</span>
        )
        response = redirect(<span class="hljs-string">'http://localhost:3000/'</span>)
        response.set_cookie(
            key=<span class="hljs-string">'auth-session'</span>,
            value=encoded_token,
            secure=<span class="hljs-literal">False</span>,
            path=<span class="hljs-string">'/'</span>
        )
        <span class="hljs-keyword">return</span> response

    <span class="hljs-keyword">return</span> redirect(<span class="hljs-string">"/"</span>)
</code></pre>
<p>When the users are redirected, Authsignal adds the JWT token in the URL as a token query parameter. </p>
<p>In this implementation, the route retrieves the token parameter from the query string. It then uses the <code>authsignal_client</code> to validate the challenge and check if the authentication was successful. </p>
<p>If the authentication succeeds, we encode a JSON Web Token (JWT). The token payload includes the <code>username</code> obtained from the challenge response. It uses the <code>SECRET_KEY</code> and the <em>HS256 algorithm</em> for encryption. </p>
<p>Next, the user is redirected to the home page (http://localhost:3000/), and a <code>auth-session</code> cookie is set with the encoded token for further user identification.</p>
<p>Note that the token returned from Authsignal in the redirect is not intended to be used as a session token. It just contains information about the challenge so that we can determine if the challenge was successful.</p>
<h3 id="heading-apilogin-route"><code>/api/login</code> Route</h3>
<p>The <code>/api/login</code> route is responsible for allowing the users to log into the application. Here’s the implementation for the route:</p>
<pre><code class="lang-python"><span class="hljs-meta">@app.route('/api/login', methods=['POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">login</span>():</span>
    username = request.json.get(<span class="hljs-string">'username'</span>)
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> username:
        <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'error'</span>: <span class="hljs-string">'Missing username parameter'</span>}), <span class="hljs-number">400</span>

    response = authsignal_client.track(
        user_id=username,
        action=<span class="hljs-string">"signIn"</span>,
        payload={
            <span class="hljs-string">"user_id"</span>: username,
            <span class="hljs-string">"redirectUrl"</span>: <span class="hljs-string">"http://localhost:5000/api/callback"</span>
        }
    )
    <span class="hljs-keyword">return</span> jsonify(response), <span class="hljs-number">200</span>
</code></pre>
<p>The route is configured to handle POST requests on the <code>/api/login</code> endpoint. Upon receiving a POST request, the route first extracts the provided <code>username</code> from the JSON payload sent with the request. It ensures that the username is present. If not, it promptly returns a 400 error response indicating a missing username parameter.</p>
<p>Similar to the signup flow, it then uses the <code>authsignal_client</code> to track the user's <strong>signIn</strong> action and generate a Magic Link. The redirectUrl specifies the URL where the user will be redirected after they have been authenticated.</p>
<h3 id="heading-apiuser-route"><strong><code>/api/user</code> Route</strong></h3>
<p>The <code>/api/user</code> route is responsible for retrieving user information. Here's the implementation for this route:</p>
<pre><code class="lang-python"><span class="hljs-meta">@app.route("/api/user", methods=['GET'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">user</span>():</span>
    token = request.cookies.get(<span class="hljs-string">'auth-session'</span>)
    decoded_token = jwt.decode(token, SECRET_KEY, algorithms=[<span class="hljs-string">"HS256"</span>])
    username = decoded_token.get(<span class="hljs-string">'username'</span>)
    response = authsignal_client.get_user(user_id=username)
    <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">"username"</span>: username, <span class="hljs-string">"email"</span>: response[<span class="hljs-string">"email"</span>]}), <span class="hljs-number">200</span>
</code></pre>
<p>In this implementation, the GET endpoint starts by extracting the auth-session cookie from the incoming request. Then it decodes the JWT using the jwt.decode method, utilizing the <code>SECRET_KEY</code> as the secret key for decoding. </p>
<p>The decoded token provides the username of the user. It then uses the <code>authsignal_client</code> to retrieve user information based on the provided <code>userId</code>. It then returns a JSON response with the username and email information.</p>
<p>By implementing these routes, we will be able to handle the basic authentication process and retrieve user information in our Flask server.</p>
<h2 id="heading-how-to-set-up-a-new-frontend-react-project">How to Set Up a New Frontend React Project</h2>
<p>Let's set up our front-end project in this section. This will also include setting up routing in the application.</p>
<p>Start by initializing a new React project using <code>create-react-app</code> or any preferred method (<a target="_blank" href="https://www.freecodecamp.org/news/complete-vite-course-for-beginners/">like Vite</a>, for example, which is a more modern way to set up a React app). This command sets up the basic structure for your React application.</p>
<pre><code class="lang-bash">npx create-react-app magic-link-auth
<span class="hljs-built_in">cd</span> magic-link-auth
</code></pre>
<p>Once the project is created and you're inside the project directory, install the required dependencies. Here, we need <code>react-router-dom</code> for handling routing and <code>bootstrap</code> for easy styling.</p>
<pre><code class="lang-bash">npm install react-router-dom bootstrap
</code></pre>
<h3 id="heading-import-bootstrap-css">Import Bootstrap CSS</h3>
<p>Bootstrap provides pre-styled components and utilities for easier and faster styling of your application.</p>
<p>In the <code>index.js</code> file, import Bootstrap:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> ReactDOM <span class="hljs-keyword">from</span> <span class="hljs-string">'react-dom/client'</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App'</span>;
<span class="hljs-keyword">import</span> reportWebVitals <span class="hljs-keyword">from</span> <span class="hljs-string">'./reportWebVitals'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"bootstrap/dist/css/bootstrap.min.css"</span>; <span class="hljs-comment">// Import Bootstrap CSS</span>

<span class="hljs-keyword">const</span> root = ReactDOM.createRoot(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>));
root.render(
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">React.StrictMode</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">React.StrictMode</span>&gt;</span></span>
);

<span class="hljs-comment">// If you want to start measuring performance in your app, pass a function</span>
<span class="hljs-comment">// to log results (for example: reportWebVitals(console.log))</span>
<span class="hljs-comment">// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals</span>
reportWebVitals();
</code></pre>
<p>Additionally, we will write some custom CSS. Replace the code in the <code>index.css</code> file with the following code:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">html</span>,
<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">font-family</span>: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}

* {
  <span class="hljs-attribute">box-sizing</span>: border-box;
}

<span class="hljs-selector-tag">main</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">5rem</span> <span class="hljs-number">0</span>;
  <span class="hljs-attribute">flex</span>: <span class="hljs-number">1</span>;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">align-items</span>: center;
}

<span class="hljs-selector-tag">code</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#fafafa</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.75rem</span>;
  <span class="hljs-attribute">font-family</span>: Menlo, Monaco, Lucida Console, Courier New, monospace;
}

<span class="hljs-selector-tag">input</span><span class="hljs-selector-attr">[type=<span class="hljs-string">"button"</span>]</span> {
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">background</span>: cornflowerblue;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">12px</span> <span class="hljs-number">24px</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">8px</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
  <span class="hljs-attribute">cursor</span>: pointer;
}
</code></pre>
<p>Similarly, replace the code in the <code>App.css</code> with the following code:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.mainContainer</span> {
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
}

<span class="hljs-selector-class">.titleContainer</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">48px</span>;
  <span class="hljs-attribute">font-weight</span>: bolder;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: center;
}

<span class="hljs-selector-class">.resultContainer</span>,
<span class="hljs-selector-class">.historyItem</span> {
  <span class="hljs-attribute">flex-direction</span>: row;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">400px</span>;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: space-between;
}

<span class="hljs-selector-class">.historyContainer</span> {
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">200px</span>;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">flex-grow</span>: <span class="hljs-number">5</span>;
  <span class="hljs-attribute">justify-content</span>: flex-start;
}

<span class="hljs-selector-class">.buttonContainer</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">260px</span>;
}

<span class="hljs-selector-class">.inputContainer</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">align-items</span>: flex-start;
  <span class="hljs-attribute">justify-content</span>: center;
}

<span class="hljs-selector-class">.inputContainer</span>&gt;<span class="hljs-selector-class">.errorLabel</span> {
  <span class="hljs-attribute">color</span>: red;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
  <span class="hljs-attribute">text-align</span>: center;
}

<span class="hljs-selector-class">.inputBox</span> {
  <span class="hljs-attribute">height</span>: <span class="hljs-number">48px</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">400px</span>;
  <span class="hljs-attribute">font-size</span>: medium;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">8px</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid grey;
  <span class="hljs-attribute">padding-left</span>: <span class="hljs-number">8px</span>;
}

<span class="hljs-selector-class">.inputButton</span> {
  <span class="hljs-attribute">height</span>: <span class="hljs-number">48px</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">400px</span>;
}
</code></pre>
<h3 id="heading-set-up-routing">Set Up Routing</h3>
<p>Routing in React applications helps navigate between different views or pages. <code>react-router-dom</code> simplifies this process.</p>
<p>In your <code>App.js</code> file, configure the routing:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { BrowserRouter, Routes, Route } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;
<span class="hljs-keyword">import</span> Dashboard <span class="hljs-keyword">from</span> <span class="hljs-string">"./pages/Dashboard"</span>;
<span class="hljs-keyword">import</span> Register <span class="hljs-keyword">from</span> <span class="hljs-string">"./pages/Register"</span>;
<span class="hljs-keyword">import</span> Login <span class="hljs-keyword">from</span> <span class="hljs-string">"./pages/Login"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./App.css"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./index.css"</span>;

<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">BrowserRouter</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Routes</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Dashboard</span> /&gt;</span>} /&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/login"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Login</span> /&gt;</span>} /&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/signup"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Register</span> /&gt;</span>} /&gt;
      <span class="hljs-tag">&lt;/<span class="hljs-name">Routes</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">BrowserRouter</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>It defines an App component that encapsulates the entire application structure within a <code>&lt;BrowserRouter&gt;</code> component. Inside <code>&lt;Routes&gt;</code>, we define three  components: one for the root path <code>/</code> rendering the <code>Dashboard</code> component, and two for the <code>/login</code> and <code>/signup</code>  paths, rendering the <code>Login</code> and <code>Register</code> components respectively. </p>
<p>This setup enables navigation between different views based on URL paths, allowing users to access specific components when they visit corresponding routes within the application.</p>
<p>In the upcoming sections, we will set up the above-mentioned three components.</p>
<h2 id="heading-how-to-set-up-the-components">How to Set Up the Components</h2>
<p>In the previous section, we imported two components from the <code>src/pages</code> folder. Let's create a <code>pages</code> folder inside the <code>src</code> folder, and then we can start creating the components.</p>
<h3 id="heading-register-component">Register Component</h3>
<p>Let’s create a <code>Register.jsx</code> file inside the pages folder. The <strong>Register</strong> component allows users to register within our application.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useNavigate, Link } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-keyword">const</span> Register = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [username, setUsername] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [usernameError, setUsernameError] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">const</span> navigate = useNavigate();

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> isAuthenticated = checkCookies();
    <span class="hljs-keyword">if</span> (isAuthenticated) {
      navigate(<span class="hljs-string">"/"</span>);
    }
  }, [navigate]);

  <span class="hljs-keyword">const</span> checkCookies = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> authSessionCookie = <span class="hljs-built_in">document</span>.cookie.match(<span class="hljs-string">"auth-session=([^;]+)"</span>);

    <span class="hljs-keyword">return</span> !!authSessionCookie;
  };

  <span class="hljs-keyword">const</span> onButtonClick = <span class="hljs-function">() =&gt;</span> {
    setUsernameError(<span class="hljs-string">""</span>);

    <span class="hljs-keyword">if</span> (<span class="hljs-string">""</span> === username) {
      setUsernameError(<span class="hljs-string">"Username is mandatory!"</span>);
      <span class="hljs-keyword">return</span>;
    }

    signup();
  };

  <span class="hljs-keyword">const</span> signup = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"http://localhost:5000/api/login"</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
      <span class="hljs-attr">headers</span>: {
        <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
      },
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
        username,
      }),
      <span class="hljs-attr">credentials</span>: <span class="hljs-string">"include"</span>,
    });

    <span class="hljs-keyword">const</span> { url } = <span class="hljs-keyword">await</span> response.json();

    <span class="hljs-comment">// Redirect to verification URL</span>
    <span class="hljs-built_in">window</span>.location.href = url;
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">mainContainer</span>"}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">titleContainer</span>"}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Sign Up<span class="hljs-tag">&lt;/<span class="hljs-name">div</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">br</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">inputContainer</span>"}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
          <span class="hljs-attr">value</span>=<span class="hljs-string">{username}</span>
          <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter your username"</span>
          <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setUsername(e.target.value)}
          className={"inputBox"}
        /&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"errorLabel text-center"</span>&gt;</span>{usernameError}<span class="hljs-tag">&lt;/<span class="hljs-name">label</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">br</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">inputContainer</span>"}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">inputButton</span>"}
          <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span>
          <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onButtonClick}</span>
          <span class="hljs-attr">value</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">Sign</span> <span class="hljs-attr">Up</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">div</span>&gt;</span>
        Existing User? <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/login"</span>&gt;</span>Login here<span class="hljs-tag">&lt;/<span class="hljs-name">Link</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">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Register;
</code></pre>
<p>The component initializes state variables using <code>useState</code> to manage the visibility of the <code>usernameError</code> and store the username input in <code>userName</code>. It also initializes the <code>navigate</code> function from <code>useNavigate</code> to handle navigation within the application.</p>
<p>We use the <code>useEffect</code> hook to check for authentication cookies when the component mounts. It calls the <code>checkCookies</code> function, which checks for the existence of cookies that we had set from the backend server. If <code>auth-session</code> cookie is found, the user is automatically redirected to the root URL using navigate("/").</p>
<p>Clicking the “Sign Up” button triggers the <code>onButtonClick</code> function. It first checks whether the user has entered the username. If not, it shows an error message using the <code>usernameError</code>. If the user has entered the username, it calls the <code>signup</code> function.</p>
<p>The <code>signup</code> performs an asynchronous POST request to the <code>/api/signup</code> endpoint with the provided username. Upon successful response, it redirects the user to the received verification URL by changing window.location.href.</p>
<p>The JSX returned by the component defines the UI layout that looks like the below:</p>
<p><img src="https://lh7-us.googleusercontent.com/gncsDRe9QVK20DWMkiWwdGOt_ZymbSVXTv-8UGvyKIpp5-TZ64-DwLGLbMtDM0B-wgXh8jNOCdkA0kia3-gJftMxFaH-za_4O0cqCSvK9GLMHSbO_nH_UfgGIf5QhHaOZg559_N0c4P9Oof4O5JmkPE" alt="Image" width="1600" height="796" loading="lazy">
<em>Register UI Component</em></p>
<p>It includes an input field for the users to enter their username and a "Sign Up" button. Below the button, we have a link to the Login page for the existing users to log in.</p>
<h3 id="heading-login-component">Login Component</h3>
<p>We have kept the login page similar to the signup page for simplicity. Hence, the Login component is pretty much the same as the Register component.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useNavigate, Link } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-keyword">const</span> Login = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [username, setUsername] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [usernameError, setUsernameError] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">const</span> navigate = useNavigate();

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> isAuthenticated = checkCookies();
    <span class="hljs-keyword">if</span> (isAuthenticated) {
      navigate(<span class="hljs-string">"/"</span>);
    }
  }, [navigate]);

  <span class="hljs-keyword">const</span> checkCookies = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> authSessionCookie = <span class="hljs-built_in">document</span>.cookie.match(<span class="hljs-string">"auth-session=([^;]+)"</span>);

    <span class="hljs-keyword">return</span> !!authSessionCookie;
  };

  <span class="hljs-keyword">const</span> onButtonClick = <span class="hljs-function">() =&gt;</span> {
    setUsernameError(<span class="hljs-string">""</span>);

    <span class="hljs-keyword">if</span> (<span class="hljs-string">""</span> === username) {
      setUsernameError(<span class="hljs-string">"Username is mandatory!"</span>);
      <span class="hljs-keyword">return</span>;
    }

    login();
  };

  <span class="hljs-keyword">const</span> login = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"http://localhost:5000/api/login"</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
      <span class="hljs-attr">headers</span>: {
        <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
      },
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
        username,
      }),
      <span class="hljs-attr">credentials</span>: <span class="hljs-string">"include"</span>,
    });

    <span class="hljs-keyword">const</span> { url } = <span class="hljs-keyword">await</span> response.json();

    <span class="hljs-comment">// Redirect to verification URL</span>
    <span class="hljs-built_in">window</span>.location.href = url;
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">mainContainer</span>"}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">titleContainer</span>"}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">div</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">br</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">inputContainer</span>"}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
          <span class="hljs-attr">value</span>=<span class="hljs-string">{username}</span>
          <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter your username"</span>
          <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(ev)</span> =&gt;</span> setUsername(ev.target.value)}
          className={"inputBox"}
        /&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"errorLabel text-center"</span>&gt;</span>{usernameError}<span class="hljs-tag">&lt;/<span class="hljs-name">label</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">br</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">inputContainer</span>"}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">inputButton</span>"}
          <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span>
          <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onButtonClick}</span>
          <span class="hljs-attr">value</span>=<span class="hljs-string">{</span>"<span class="hljs-attr">Log</span> <span class="hljs-attr">in</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">div</span>&gt;</span>
        New User? <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/signup"</span>&gt;</span>Sign up here<span class="hljs-tag">&lt;/<span class="hljs-name">Link</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">div</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Login;
</code></pre>
<p>The only significant difference other than the text and button in the UI here is that we will be making the API call to the <code>/api/login</code> route when the users hit the Login button.</p>
<p>The UI looks like below:</p>
<p><img src="https://lh7-us.googleusercontent.com/F4EDOhuMbTRtXt3UxhpLYdZPT8XtCuYWjEX3H95bEXEZatLDsuRxaut3KZ3ZtyHSWIQ1WkuA5WWUxZcXrCBHyCMfMG-LQTKjnQHXyY8Y2Ha93YstE0Kycd9ji9lj33wMc1D8Km7pFNu5EGyQh14D9m8" alt="Image" width="1600" height="798" loading="lazy">
<em>Login UI Component</em></p>
<h3 id="heading-dashboard-component">Dashboard Component</h3>
<p>The Dashboard component in our application serves as the interface for authenticated users, displaying a welcome message with the user’s email and enabling user logout functionality. </p>
<p>Let’s create a <code>Dashboard.jsx</code> file inside the <code>pages</code> folder.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useNavigate } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router-dom"</span>;

<span class="hljs-keyword">const</span> Dashboard = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [userEmail, setUserEmail] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> navigate = useNavigate();

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> checkCookies = <span class="hljs-keyword">async</span> () =&gt; {
      <span class="hljs-keyword">const</span> authSessionCookie = <span class="hljs-built_in">document</span>.cookie.match(<span class="hljs-string">"auth-session=([^;]+)"</span>);

      <span class="hljs-keyword">if</span> (!authSessionCookie) {
        navigate(<span class="hljs-string">"/auth"</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
      }

      <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    };

    <span class="hljs-keyword">const</span> fetchData = <span class="hljs-keyword">async</span> () =&gt; {
      <span class="hljs-keyword">const</span> cookiesValid = <span class="hljs-keyword">await</span> checkCookies();
      <span class="hljs-keyword">if</span> (!cookiesValid) <span class="hljs-keyword">return</span>;

      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">"http://localhost:5000/api/user"</span>, {
          <span class="hljs-attr">method</span>: <span class="hljs-string">"GET"</span>,
          <span class="hljs-attr">headers</span>: {
            <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
          },
          <span class="hljs-attr">credentials</span>: <span class="hljs-string">"include"</span>
        });

        <span class="hljs-keyword">if</span> (!response.ok) {
          <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Failed to fetch user data"</span>);
        }

        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json();
        setUserEmail(data.email);
      } <span class="hljs-keyword">catch</span> (error) {
        navigate(<span class="hljs-string">"/auth"</span>);
      }
    };

    fetchData();
  }, [navigate]);

  <span class="hljs-keyword">const</span> handleLogout = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">document</span>.cookie = <span class="hljs-string">`auth-session=; max-age=0`</span>;
    navigate(<span class="hljs-string">"/auth"</span>);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"d-flex justify-content-center align-items-center vh-100"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-3 text-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Welcome Home!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"lead"</span>&gt;</span>You're logged in as {userEmail}!<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"d-flex justify-content-center"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"btn btn-lg btn-dark fw-bold border-white bg-dark"</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleLogout}</span>
          &gt;</span>
            Log Out
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</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">main</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> Dashboard;
</code></pre>
<p>Upon component mounting or when <code>navigate</code> changes (a dependency of <code>useEffect</code>), the effect runs. It begins by defining two asynchronous functions. The first, <code>checkCookies</code>, verifies the presence of <code>auth-session</code> cookies. If it is missing, it redirects the user to the authentication route.</p>
<p>The second function, <code>fetchData</code>, is responsible for fetching user data. It checks the validity of cookies using <code>checkCookies</code>. Upon verification, it sends a GET request to our back-end API endpoint. Upon successful response, it updates the <code>userEmail</code> state with the user's email fetched from the API data. </p>
<p>If any error occurs during this process, such as failing to fetch user data, it redirects the user back to the authentication route.</p>
<p>The JSX returned by the component renders a simple dashboard layout.</p>
<p><img src="https://lh7-us.googleusercontent.com/ca-epjkteyLzE_dVbbWN6bC5fMVogCJLRvR8Milfjl7UzoHRK7462_YJZhkJvhoTpBtD0sNFwpGbNaLTKNEuBg6wKSxv6j5-ApmjpOtPgx-UkeM8i39A0KwAuU3L2TeRc8R_3aXugnAMcH4iGrlBP0M" alt="Image" width="1600" height="797" loading="lazy">
<em>Dashboard UI Component</em></p>
<p>The displayed content includes a welcoming message and the currently logged-in user's email. There's also a "Log Out" button which, upon clicking, initiates the logout process by triggering the <code>handleLogout</code> function. It removes the <code>auth-session</code> cookies by setting their max-age to 0, effectively expiring them. Afterward, it redirects the user to the authentication route.</p>
<h2 id="heading-how-to-run-the-application">How to Run the Application</h2>
<p>You can find the code of the final application in this <a target="_blank" href="https://github.com/ashutoshkrris/authsignal-magic-link-demo">GitHub repository</a>. To run your backend application, run <code>python app.py</code> from your back-end folder in your terminal. This will start your back-end server on port 5000. Next, run the frontend application using the <code>npm start</code> command. This will start your frontend application on the port 3000.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>In this tutorial, you learned how to use Authsignal to implement basic user authentication with email verification through magic links. </p>
<p>Authsignal makes handling users and keeping things safe easier, letting developers focus on improving apps. It also removes the overhead of remembering another password for the users of the application.</p>
<p>To learn more about Authsignal, <a target="_blank" href="https://docs.authsignal.com/">visit the Authsignal documentation</a>.</p>
<p>Should you have any issues or questions related to the tutorial, then feel free to reach out to me on <a target="_blank" href="https://twitter.com/ashutoshkrris">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Python One-Liners to Help You Write Simple, Readable Code ]]>
                </title>
                <description>
                    <![CDATA[ Python's beauty lies in its simplicity and readability. And mastering the art of writing concise yet powerful code can significantly enhance your productivity as a developer. I'm talking about really short lines of code that do big things. In this ar... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/python-one-liners/</link>
                <guid isPermaLink="false">66ba0eb29065919bb4e84ca3</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Tue, 28 Nov 2023 16:58:20 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/11/one-liners-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Python's beauty lies in its simplicity and readability. And mastering the art of writing concise yet powerful code can significantly enhance your productivity as a developer. I'm talking about really short lines of code that do big things.</p>
<p>In this article, we'll explore 8 essential Python one-liners that every Pythonista should have in their toolkit. From list comprehensions to lambda functions and beyond, these techniques offer elegant solutions to common programming challenges, helping you write cleaner, more efficient code.</p>
<h2 id="heading-list-comprehension">List Comprehension</h2>
<p>List comprehension is a Pythonic way to create lists with a single line of code. It offers a concise alternative to traditional loops, enabling you to generate lists quickly and efficiently.</p>
<p>Let's say you want to create a list containing squares of numbers from 0 to 9. Using a traditional loop, you'd do it like this:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Using a traditional loop</span>
squared_numbers = []
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">10</span>):
    squared_numbers.append(i ** <span class="hljs-number">2</span>)
print(squared_numbers)
</code></pre>
<p>The traditional loop method requires more lines of code and explicitly defines the iteration process, appending each squared number to the list step by step.</p>
<p>On the other hand, list comprehension can achieve the same result in a single line, making the code more concise and readable. It condenses the loop into a clear, compact structure, generating the squared numbers directly into a list.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Using list comprehension</span>
squared_numbers = [i ** <span class="hljs-number">2</span> <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">10</span>)]
print(squared_numbers)
</code></pre>
<p>You can use list comprehensions when you need to apply a simple operation to every element in a sequence, such as transforming a list of numbers or strings.</p>
<p>You can learn how you can pack and destructure lists in Python <a target="_blank" href="https://blog.ashutoshkrris.in/mastering-list-destructuring-and-packing-in-python-a-comprehensive-guide">here</a>.</p>
<h2 id="heading-lambda-functions">Lambda Functions</h2>
<p><a target="_blank" href="https://blog.ashutoshkrris.in/mastering-lambdas-a-guide-to-anonymous-functions-in-python">Lambda functions</a>, also known as anonymous functions, allow you to create small, throwaway functions without explicitly defining them with <code>def</code>. They are particularly useful in scenarios where a function is needed for a short operation.</p>
<p>First, let's look at an example using <code>def</code>:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Using def</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add_numbers</span>(<span class="hljs-params">x, y</span>):</span>
    <span class="hljs-keyword">return</span> x + y

print(add_numbers(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>))
</code></pre>
<p>In this code, the <code>def</code> keyword is used to define a named function <code>add_numbers</code> explicitly. It takes an argument <code>x</code> and <code>y</code> and returns the sum of them. This traditional approach provides a named function that can be called multiple times.</p>
<p>But when you need a function just for one-time usage, you can just define an anonymous function using the <code>lambda</code> keyword like this:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Using Lambda</span>
add = <span class="hljs-keyword">lambda</span> x, y: x + y
print(add(<span class="hljs-number">2</span>, <span class="hljs-number">3</span>))
</code></pre>
<p>It achieves the same result as <code>add_numbers</code> but in a single line without assigning a name explicitly. Lambda functions are useful for short, throwaway functions that are used infrequently or as part of other expressions.</p>
<h2 id="heading-map-and-filter">Map and Filter</h2>
<p>The <code>map</code> and <code>filter</code> functions are powerful tools for working with iterables, allowing concise manipulation and filtering of data.</p>
<p>Let's say you have a list of strings and you want to convert each item of the list into uppercase.</p>
<pre><code class="lang-python">fruits = [<span class="hljs-string">'apple'</span>, <span class="hljs-string">'banana'</span>, <span class="hljs-string">'cherry'</span>]
upper_case_loop = []
<span class="hljs-keyword">for</span> fruit <span class="hljs-keyword">in</span> fruits:
    upper_case_loop.append(fruit.upper())
print(upper_case_loop)
</code></pre>
<p>Now, you can achieve the same using the <code>map</code> function:</p>
<pre><code class="lang-python">upper_case = list(map(<span class="hljs-keyword">lambda</span> x: x.upper(), [<span class="hljs-string">'apple'</span>, <span class="hljs-string">'banana'</span>, <span class="hljs-string">'cherry'</span>]))
</code></pre>
<p>You can utilize <code>map</code> when you need to perform an operation on every element of an iterable. <code>filter</code> is handy for selectively choosing elements based on a condition.</p>
<p>You can learn more about the <code>map</code>, <code>filter</code> and <code>reduce</code> functions <a target="_blank" href="https://blog.ashutoshkrris.in/mastering-lambdas-a-guide-to-anonymous-functions-in-python#heading-using-lambda-functions-as-arguments-in-higher-order-functions-map-filter-reduce">here</a>.</p>
<h2 id="heading-ternary-operator">Ternary Operator</h2>
<p>The ternary operator provides a condensed way to write conditional statements in a single line, enhancing code readability.</p>
<p>Let's say, you have a number and you want to check if it's even or odd. You can do it using the traditional if condition as below:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Traditional if</span>
result = <span class="hljs-literal">None</span>
num = <span class="hljs-number">5</span>
<span class="hljs-keyword">if</span> num % <span class="hljs-number">2</span> == <span class="hljs-number">0</span>:
    result = <span class="hljs-string">"Even"</span>
<span class="hljs-keyword">else</span>:
    result = <span class="hljs-string">"Odd"</span>
</code></pre>
<p>But you can achieve the same results in a single line using the ternary operator:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Ternary Operator</span>
num = <span class="hljs-number">7</span>
result = <span class="hljs-string">"Even"</span> <span class="hljs-keyword">if</span> num % <span class="hljs-number">2</span> == <span class="hljs-number">0</span> <span class="hljs-keyword">else</span> <span class="hljs-string">"Odd"</span>
</code></pre>
<p>When you need to assign values based on conditions, especially in situations requiring simple if-else checks, the ternary operator shines.</p>
<h2 id="heading-zip-function">Zip Function</h2>
<p>The <code>zip</code> function enables you to combine multiple iterables element-wise, forming tuples of corresponding elements.</p>
<p>Let's assume you have two lists: one containing the names of students and the other containing their respective grades for a specific assignment.</p>
<pre><code class="lang-python">students = [<span class="hljs-string">'Dilli'</span>, <span class="hljs-string">'Vikram'</span>, <span class="hljs-string">'Rolex'</span>, <span class="hljs-string">'Leo'</span>]
grades = [<span class="hljs-number">85</span>, <span class="hljs-number">92</span>, <span class="hljs-number">78</span>, <span class="hljs-number">88</span>]
</code></pre>
<p>Now, you want to create a report that pairs each student's name with their grade for easy comprehension or further analysis. You can do it by iterating over the list and appending them to a new list as below:</p>
<pre><code class="lang-python">students = [<span class="hljs-string">'Dilli'</span>, <span class="hljs-string">'Vikram'</span>, <span class="hljs-string">'Rolex'</span>, <span class="hljs-string">'Leo'</span>]
grades = [<span class="hljs-number">85</span>, <span class="hljs-number">92</span>, <span class="hljs-number">78</span>, <span class="hljs-number">88</span>]

student_grade_pairs = []
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(students)):
    student_grade_pairs.append((students[i], grades[i]))

print(student_grade_pairs)
</code></pre>
<p>The above loop method manually pairs elements from two lists by iterating through their indices, accessing elements at the same positions, and appending tuples of those elements into a new list <code>student_grade_pairs</code>.</p>
<p>But, what if I tell you that we can achieve the same pairing effect in one line using the <code>zip</code> function as below:</p>
<pre><code class="lang-python">students = [<span class="hljs-string">'Dilli'</span>, <span class="hljs-string">'Vikram'</span>, <span class="hljs-string">'Rolex'</span>, <span class="hljs-string">'Leo'</span>]
grades = [<span class="hljs-number">85</span>, <span class="hljs-number">92</span>, <span class="hljs-number">78</span>, <span class="hljs-number">88</span>]

student_grade_pairs = list(zip(students, grades))
print(student_grade_pairs)
</code></pre>
<p>The <code>zip</code> function elegantly combines elements from both lists, creating pairs of corresponding elements as tuples. The result <code>student_grade_pairs</code> is a list of tuples, where each tuple contains an element from the grades list paired with the corresponding element from the students list.</p>
<p>You can learn more about the <code>zip</code>  function <a target="_blank" href="https://blog.ashutoshkrris.in/zipping-through-python-a-comprehensive-guide-to-the-zip-function">here</a>.</p>
<h2 id="heading-enumerate-function">Enumerate Function</h2>
<p>The <code>enumerate</code> function offers a concise way to iterate over a sequence while keeping track of the index.</p>
<p>Let's say you're developing a feature where users can add items to their shopping list, and you want to display the items along with their position or index in the list for easy reference.</p>
<p>You can do it using a traditional for-loop as below:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Simulating a grocery list</span>
grocery_list = [<span class="hljs-string">'Apples'</span>, <span class="hljs-string">'Milk'</span>, <span class="hljs-string">'Bread'</span>, <span class="hljs-string">'Eggs'</span>, <span class="hljs-string">'Cheese'</span>]

<span class="hljs-comment"># Displaying the grocery list with indices</span>
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(grocery_list)):
    print(<span class="hljs-string">f"<span class="hljs-subst">{i}</span>. <span class="hljs-subst">{grocery_list[i]}</span>"</span>)
</code></pre>
<p>The traditional loop with manual indexing involves using <code>range</code> along with <code>len</code> to generate indices that are then used to access elements in the <code>grocery_list</code> list. This method requires more code and is less readable due to the explicit handling of indices.</p>
<p>The <code>enumerate</code> function simplifies the process by directly providing both indices and elements from the <code>grocery_list</code> list.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Simulating a grocery list</span>
grocery_list = [<span class="hljs-string">'Apples'</span>, <span class="hljs-string">'Milk'</span>, <span class="hljs-string">'Bread'</span>, <span class="hljs-string">'Eggs'</span>, <span class="hljs-string">'Cheese'</span>]

<span class="hljs-comment"># Displaying the grocery list with indices</span>
<span class="hljs-keyword">for</span> index, item <span class="hljs-keyword">in</span> enumerate(grocery_list):
    print(<span class="hljs-string">f"<span class="hljs-subst">{index}</span>. <span class="hljs-subst">{item}</span>"</span>)
</code></pre>
<p>It's concise, readable, and more Pythonic, eliminating the need for manual index handling and making the code cleaner. This approach is generally preferred for its simplicity and clarity in obtaining indices and elements from an iterable.</p>
<h2 id="heading-string-join">String Join</h2>
<p>The <code>join</code> method is a clean way to concatenate strings from an iterable into a single string.</p>
<p>Suppose you have a list of words and want to create a sentence by joining these words using traditional concatenation. You'd do it as below:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Using traditional concatenation</span>
words = [<span class="hljs-string">'Python'</span>, <span class="hljs-string">'is'</span>, <span class="hljs-string">'awesome'</span>, <span class="hljs-string">'and'</span>, <span class="hljs-string">'powerful'</span>]

sentence = <span class="hljs-string">''</span>
<span class="hljs-keyword">for</span> word <span class="hljs-keyword">in</span> words:
    sentence += word + <span class="hljs-string">' '</span>

print(sentence.strip())  <span class="hljs-comment"># Strip to remove the trailing space</span>
</code></pre>
<p>In the traditional concatenation method, a loop iterates through the list of words, and each word is concatenated with a space. But this approach requires creating a new string for each concatenation operation, which might not be efficient for larger strings due to string immutability.</p>
<p>The <code>join</code> method, on the other hand, is more efficient and concise. It joins the elements of the list using the specified separator (in this case, a space), creating the sentence in a single operation.</p>
<pre><code class="lang-python">.<span class="hljs-comment"># Using join method</span>
words = [<span class="hljs-string">'Python'</span>, <span class="hljs-string">'is'</span>, <span class="hljs-string">'awesome'</span>, <span class="hljs-string">'and'</span>, <span class="hljs-string">'powerful'</span>]

sentence = <span class="hljs-string">' '</span>.join(words)
print(sentence)
</code></pre>
<p>This method is generally the preferred way to join strings in Python due to its efficiency and readability.</p>
<h2 id="heading-unpacking-lists">Unpacking Lists</h2>
<p>Python's unpacking feature allows for efficient assignment of elements from iterables to variables. </p>
<p>Suppose you have a list of numbers, and you want to assign each number to separate variables using traditional indexing.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Using traditional unpacking</span>
numbers = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]

a = numbers[<span class="hljs-number">0</span>]
b = numbers[<span class="hljs-number">1</span>]
c = numbers[<span class="hljs-number">2</span>]

print(a, b, c)
</code></pre>
<p>In the traditional unpacking method, individual elements from the list are accessed and assigned to separate variables by explicitly indexing each element. This method is more verbose and requires knowing the number of elements in advance.</p>
<p>Now, let's accomplish the same using the <code>*</code> operator for unpacking the list into variables.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Using * operator for unpacking</span>
numbers = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]

a, b, c = numbers

print(a, b, c)
</code></pre>
<p>You can learn more about the <code>*</code> operator and list unpacking in <a target="_blank" href="https://blog.ashutoshkrris.in/mastering-list-destructuring-and-packing-in-python-a-comprehensive-guide#heading-destructuring-assignment">this tutorial</a>.</p>
<h2 id="heading-should-you-always-use-one-liners">Should You Always Use One-Liners?</h2>
<p>While Python one-liners offer conciseness and elegance, there are considerations to keep in mind before applying them universally:</p>
<ol>
<li><strong>Readability</strong>: One-liners might sacrifice readability for crispness. Complex one-liners can be hard to understand, especially for newcomers or when revisiting code after some time. </li>
<li><strong>Maintainability</strong>: Overuse of one-liners, especially complex ones, can make code maintenance challenging. Debugging and modifying concise code might be more difficult.</li>
<li><strong>Performance</strong>: In certain scenarios, one-liners might not be the most performant solution. These concise expressions may consume more resources, such as memory or CPU, and their underlying operations might have higher time complexity, affecting efficiency, especially with large datasets or intensive computations.</li>
<li><strong>Debugging</strong>: Debugging a one-liner can be more challenging due to its compactness. Identifying issues or errors might take longer compared to well-structured, multiple-line code.</li>
<li><strong>Context</strong>: Not all situations warrant one-liners. Sometimes, a straightforward, explicit approach might be more suitable for code clarity, especially when working in teams.</li>
</ol>
<p>Ultimately, the decision to use one-liners should consider the trade-offs between conciseness and readability. Strive for a balance that enhances code clarity without compromising maintainability and understanding, especially when collaborating or working on larger projects.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Mastering Python's concise techniques like list comprehensions, lambda functions, <code>enumerate</code>, <code>join</code>, <code>zip</code>, and unpacking with the <code>*</code> operator can significantly enhance code readability, efficiency, and simplicity. These methods offer elegant solutions to common programming challenges, reducing verbosity and improving code maintainability. </p>
<p>Understanding when and how to use these Pythonic constructs empowers developers to write cleaner, more expressive code and enhancing overall productivity in various programming scenarios.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Implement Two-Factor Authentication with PyOTP and Google Authenticator in Your Flask App ]]>
                </title>
                <description>
                    <![CDATA[ Two-Factor Authentication, or 2FA, is like having an extra lock on the door to your online accounts. Instead of just using a password, 2FA adds another layer of security. It's a bit like needing both a key and a special code to open a vault. Think of... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-implement-two-factor-authentication-in-your-flask-app/</link>
                <guid isPermaLink="false">66ba0e98102ebf67c0a6d42c</guid>
                
                    <category>
                        <![CDATA[ authentication ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flask Framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Two-factor authentication ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Mon, 27 Nov 2023 21:41:12 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/11/2fa-tutorial.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Two-Factor Authentication, or 2FA, is like having an extra lock on the door to your online accounts. Instead of just using a password, 2FA adds another layer of security. It's a bit like needing both a key and a special code to open a vault.</p>
<p>Think of it as a shield for your accounts. Passwords can sometimes be guessed or stolen, but with 2FA, even if someone gets your password, they'd still need that extra code or device to get in. It's an extra step that makes your accounts much harder for hackers to break into.</p>
<p>So, let's explore how to set up this extra layer of protection using PyOTP and Google Authenticator in your Flask app. </p>
<h3 id="heading-table-of-contents">Table of Contents:</h3>
<ol>
<li><a class="post-section-overview" href="#heading-overview-of-pyotp-and-google-authenticator">Overview of PyOTP and Google Authenticator</a></li>
<li><a class="post-section-overview" href="#heading-two-factor-authentication-workflow-in-our-application">Two-Factor Authentication Workflow in Our Application</a></li>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-get-your-tools-ready">Get Your Tools Ready</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-the-project">How to Set Up the Project</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-blueprints-for-accounts-and-core">How to Create Blueprints for Accounts and Core</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-user-model">How to Create a User Model</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-flask-login">How to Add Flask-Login</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-templates-and-static-files">How to Add Templates and Static Files</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-the-homepage">How to Create the Homepage</a></li>
<li><a class="post-section-overview" href="#heading-how-to-implement-user-registration">How to Implement User Registration</a></li>
<li>[How to Implement User Login](#<h2 id="how-to-implement-user-login"><strong>How to Implement User Login</strong></h2>)</li>
<li><a class="post-section-overview" href="#heading-how-to-log-out-the-users">How to Log Out the Users</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-the-setup-2fa-page">How to Add the Setup 2FA Page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-a-2fa-verification-page">How to Add a 2FA Verification Page</a></li>
<li><a class="post-section-overview" href="#heading-how-to-run-the-completed-app-for-the-first-time">How to Run the Completed App for the First Time</a></li>
<li><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></li>
</ol>
<h2 id="heading-overview-of-pyotp-and-google-authenticator">Overview of PyOTP and Google Authenticator</h2>
<p>PyOTP is a Python library that's incredibly handy for generating Time-based One-Time Passwords (TOTP) and HMAC-based One-Time Passwords (HOTP). Its primary role revolves around creating these unique, time-sensitive codes that add an extra layer of security to user accounts. </p>
<p>By integrating PyOTP into your Flask application, you can easily implement Two-Factor Authentication (2FA) by generating and verifying these OTPs.</p>
<p>If you're new to PyOTP or would like a refresher on its functionalities, I recommend reviewing my previous <a target="_blank" href="https://blog.ashutoshkrris.in/how-to-generate-otps-using-pyotp-in-python">guide on PyOTP</a>. This understanding will be beneficial as we get into the integration of PyOTP within your Flask application for Two-Factor Authentication (2FA).</p>
<p>Google Authenticator, on the other hand, stands out as one of the most widely used OTP generator apps available. It functions as a secure platform for generating time-based OTPs, compatible with various services and applications supporting 2FA. Users can easily set up Google Authenticator on their devices to generate these time-sensitive codes, adding an extra level of security to their accounts.</p>
<h2 id="heading-two-factor-authentication-workflow-in-our-application">Two-Factor Authentication Workflow in Our Application</h2>
<p>Here's a breakdown of the flow of two-factor authentication in our application:</p>
<ol>
<li><strong>Registration with 2FA Setup</strong>: When users sign up on our website, they're prompted to set up an extra layer of security—2FA. This involves scanning a QR code using an authenticator app, such as Google Authenticator, to link their account securely.</li>
<li><strong>Login Initiation</strong>: When users return to log in, they start by entering their usual email/username and password combo to access their account.</li>
<li><strong>Extra Security Check</strong>: Before granting access, our website throws in an additional hurdle: users need to provide an OTP (One-Time Password) displayed on their authenticator app. This ensures they're not just entering the password but also confirming their identity with a unique, time-sensitive code.</li>
<li><strong>Validation and Authorization</strong>: The user inputs the received OTP into our platform. The system then double-checks this OTP against the expected code, validating the information. If the OTP matches, it's like handing over the secret handshake, granting the user access to their account.</li>
</ol>
<p>This seamless back-and-forth between passwords, authenticator apps, and unique codes ensures that only the rightful account owner can access the precious content behind the digital doors of your website.</p>
<p>If you also enjoy visual learning, here's a fancy video showing how the app does its thing.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/qzLcbq5-UNA" 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>Now, let's get to some coding!</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you get started with the tutorial, make sure you have the following requirements satisfied:</p>
<ul>
<li>Working knowledge of Python</li>
<li>Python 3.8+ installed on your system</li>
<li>Basic knowledge of <a target="_blank" href="https://ashutoshkrris.hashnode.dev/getting-started-with-flask">Flask</a> and <a target="_blank" href="https://ashutoshkrris.hashnode.dev/how-to-use-blueprints-to-organize-your-flask-apps">Flask Blueprints</a></li>
<li>Knowledge of <a target="_blank" href="https://blog.ashutoshkrris.in/how-to-set-up-basic-user-authentication-in-a-flask-app">basic authentication in Flask</a> (optional)</li>
</ul>
<h2 id="heading-get-your-tools-ready"><strong>Get Your Tools Ready</strong></h2>
<p>You'll need a few external libraries for this project. Let's learn more about them and install them one by one.</p>
<p>But before we install them, let's create a virtual environment and activate it.</p>
<p>First, start with creating the project directory and navigating to it like this:</p>
<pre><code class="lang-bash">mkdir flask-two-factor-auth
ccd flask-two-factor-auth
</code></pre>
<p>We are going to create a virtual environment using <code>venv</code>. Python now ships with a pre-installed <code>venv</code> library. So, to create a virtual environment, you can use the below command:</p>
<pre><code class="lang-bash">python -m venv env
</code></pre>
<p>The above command will create a virtual environment named env. Now, we need to activate the environment using this command:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> env/Scripts/activate
</code></pre>
<p>To verify if the environment has been activated or not, you can see <code>(env)</code> in your terminal. Now, we can install the libraries.</p>
<ul>
<li><a target="_blank" href="https://flask.palletsprojects.com/en/2.2.x/">Flask</a> is a simple, easy-to-use microframework for Python that helps you build scalable and secure web applications.</li>
<li><a target="_blank" href="https://flask-login.readthedocs.io/en/latest/">Flask-Login</a> provides user session management for Flask. It handles the common tasks of logging in, logging out, and remembering your users’ sessions over extended periods of time.</li>
<li><a target="_blank" href="https://flask-bcrypt.readthedocs.io/en/1.0.1/">Flask-Bcrypt</a> is a Flask extension that provides bcrypt hashing utilities for your application.</li>
<li><a target="_blank" href="https://flask-wtf.readthedocs.io/en/1.0.x/">Flask-WTF</a> is a simple integration of Flask and WTForms that helps you create forms in Flask.</li>
<li><a target="_blank" href="https://flask-migrate.readthedocs.io/en/latest/">Flask-Migrate</a> is an extension that handles SQLAlchemy database migrations for Flask applications using Alembic. The database operations are made available through the Flask command-line interface.</li>
<li><a target="_blank" href="https://flask-sqlalchemy.palletsprojects.com/en/2.x/">Flask-SQLAlchemy</a> is an extension for Flask that adds support for SQLAlchemy to your application. It helps you simplify things using SQLAlchemy with Flask by giving you useful defaults and extra helpers that make it easier to perform common tasks.</li>
<li><a target="_blank" href="https://blog.ashutoshkrris.in/how-to-generate-otps-using-pyotp-in-python">PyOTP</a> helps you generate OTPs using Time-based OTP (TOTP) and HMAC-based OTP (HOTP) algorithms effortlessly.</li>
<li><a target="_blank" href="https://pypi.org/project/qrcode/">QRCode</a> helps you generate QR Codes in Python</li>
<li><a target="_blank" href="https://pypi.org/project/python-decouple/">Python Decouple</a> helps you use environment variables in your Python project.</li>
</ul>
<p>To install the above-mentioned libraries all in one go, run the following command:</p>
<pre><code class="lang-bash">pip install Flask Flask-Login Flask-Bcrypt Flask-WTF FLask-Migrate Flask-SQLAlchemy pyotp qrcode python-decouple
</code></pre>
<h2 id="heading-how-to-set-up-the-project"><strong>How to Set Up the Project</strong></h2>
<p>Let’s start by creating a <code>src</code> directory:</p>
<pre><code class="lang-bash">mkdir src
</code></pre>
<p>The first file will be the <code>__init__.py</code> file for the project:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> decouple <span class="hljs-keyword">import</span> config
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask
<span class="hljs-keyword">from</span> flask_bcrypt <span class="hljs-keyword">import</span> Bcrypt
<span class="hljs-keyword">from</span> flask_migrate <span class="hljs-keyword">import</span> Migrate
<span class="hljs-keyword">from</span> flask_sqlalchemy <span class="hljs-keyword">import</span> SQLAlchemy

app = Flask(__name__)
app.config.from_object(config(<span class="hljs-string">"APP_SETTINGS"</span>))

bcrypt = Bcrypt(app)
db = SQLAlchemy(app)
migrate = Migrate(app, db)

<span class="hljs-comment"># Registering blueprints</span>
<span class="hljs-keyword">from</span> src.accounts.views <span class="hljs-keyword">import</span> accounts_bp
<span class="hljs-keyword">from</span> src.core.views <span class="hljs-keyword">import</span> core_bp

app.register_blueprint(accounts_bp)
app.register_blueprint(core_bp)
</code></pre>
<p>In the above script, we created a Flask app called <code>app</code> . We use the <code>__name__</code> argument to indicate the app's module or package so that Flask knows where to find other files such as templates. We also set the configuration of the app using an environment variable called <code>APP_SETTINGS</code>. We'll export it later.</p>
<p>To use Flask-Bcrypt, Flask-SQLAlchemy, and Flask-Migrate in our application, we just need to create objects of the <code>Bcrypt</code>, <code>SQLAlchemy</code> and <code>Migrate</code> classes from the <code>flask_bcrypt</code>, <code>flask_sqlalchemy</code> and, <code>flask_migrate</code> libraries, respectively.</p>
<p>We've also registered blueprints called <code>accounts_bp</code> and <code>core_bp</code> in the application. We'll define them later in the tutorial.</p>
<p>In the root directory of the project (that is, outside the <code>src</code> directory), create a file called <code>config.py</code>. We'll store the configurations for the project in this file. Within the file, add the following content:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> decouple <span class="hljs-keyword">import</span> config

DATABASE_URI = config(<span class="hljs-string">"DATABASE_URL"</span>)
<span class="hljs-keyword">if</span> DATABASE_URI.startswith(<span class="hljs-string">"postgres://"</span>):
    DATABASE_URI = DATABASE_URI.replace(<span class="hljs-string">"postgres://"</span>, <span class="hljs-string">"postgresql://"</span>, <span class="hljs-number">1</span>)


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span>(<span class="hljs-params">object</span>):</span>
    DEBUG = <span class="hljs-literal">False</span>
    TESTING = <span class="hljs-literal">False</span>
    CSRF_ENABLED = <span class="hljs-literal">True</span>
    SECRET_KEY = config(<span class="hljs-string">"SECRET_KEY"</span>, default=<span class="hljs-string">"guess-me"</span>)
    SQLALCHEMY_DATABASE_URI = DATABASE_URI
    SQLALCHEMY_TRACK_MODIFICATIONS = <span class="hljs-literal">False</span>
    BCRYPT_LOG_ROUNDS = <span class="hljs-number">13</span>
    WTF_CSRF_ENABLED = <span class="hljs-literal">True</span>
    DEBUG_TB_ENABLED = <span class="hljs-literal">False</span>
    DEBUG_TB_INTERCEPT_REDIRECTS = <span class="hljs-literal">False</span>
    APP_NAME = config(<span class="hljs-string">"APP_NAME"</span>)


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DevelopmentConfig</span>(<span class="hljs-params">Config</span>):</span>
    DEVELOPMENT = <span class="hljs-literal">True</span>
    DEBUG = <span class="hljs-literal">True</span>
    WTF_CSRF_ENABLED = <span class="hljs-literal">False</span>
    DEBUG_TB_ENABLED = <span class="hljs-literal">True</span>


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestingConfig</span>(<span class="hljs-params">Config</span>):</span>
    TESTING = <span class="hljs-literal">True</span>
    DEBUG = <span class="hljs-literal">True</span>
    SQLALCHEMY_DATABASE_URI = <span class="hljs-string">"sqlite:///testdb.sqlite"</span>
    BCRYPT_LOG_ROUNDS = <span class="hljs-number">1</span>
    WTF_CSRF_ENABLED = <span class="hljs-literal">False</span>


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProductionConfig</span>(<span class="hljs-params">Config</span>):</span>
    DEBUG = <span class="hljs-literal">False</span>
    DEBUG_TB_ENABLED = <span class="hljs-literal">False</span>
</code></pre>
<p>In the above script, we have created a <code>Config</code> class and defined various attributes inside that. Also, we have created different child classes (as per different stages of development) that inherit the <code>Config</code> class.</p>
<p>Notice that we're using a few environment variables like <code>SECRET_KEY</code>, <code>DATABASE_URL</code>, and <code>APP_NAME</code>. Create a file named <code>.env</code> in the root directory and add the following content there:</p>
<pre><code class="lang-python">export SECRET_KEY=fdkjshfhjsdfdskfdsfdcbsjdkfdsdf
export DEBUG=<span class="hljs-literal">True</span>
export APP_SETTINGS=config.DevelopmentConfig
export DATABASE_URL=sqlite:///db.sqlite
export FLASK_APP=src
export FLASK_DEBUG=<span class="hljs-number">1</span>
export APP_NAME=<span class="hljs-string">"Flask User Authentication App"</span>
</code></pre>
<p>Apart from the <code>SECRET_KEY</code> , <code>DATABASE_URL</code> and <code>APP_NAME</code>, we've also exported <code>APP_SETTINGS</code>, <code>DEBUG</code>, <code>FLASK_APP</code>, and <code>FLASK_DEBUG</code>.</p>
<p>The <code>APP_SETTINGS</code> refers to one of the classes we created in the <code>config.py</code> file. We set it to the current stage of the project.</p>
<p>The value of <code>FLASK_APP</code> is the name of the package we have created. Since the app is in the development stage, you can set the values of <code>DEBUG</code> and <code>FLASK_DEBUG</code> to <code>True</code> and <code>1</code>, respectively.</p>
<p>Run the following command to export all the environment variables from the <code>.env</code> file:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> .env
</code></pre>
<p>Next, we'll create a CLI application of the app so that we can later add custom commands if required.</p>
<p>Create a <code>manage.py</code> file in the root directory of the application and add the following code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask.cli <span class="hljs-keyword">import</span> FlaskGroup

<span class="hljs-keyword">from</span> src <span class="hljs-keyword">import</span> app

cli = FlaskGroup(app)


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    cli()
</code></pre>
<p>Now, your basic application is ready. You can run it using the following command:</p>
<pre><code class="lang-bash">python manage.py run
</code></pre>
<p>Your file structure should look like below as of now:</p>
<pre><code class="lang-bash">flask-two-factor-auth/
├── src/
│   └── __init__.py
├── .env
├── config.py
└── manage.py
</code></pre>
<h2 id="heading-how-to-create-blueprints-for-accounts-and-core"><strong>How to Create Blueprints for Accounts and Core</strong></h2>
<p>As mentioned earlier, you'll use the concepts of blueprints in the project. Let's create two blueprints – <code>accounts_bp</code> and <code>core_bp</code> – in this section.</p>
<p>First create a directory called <code>accounts</code> like this:</p>
<pre><code class="lang-bash">mkdir accounts
<span class="hljs-built_in">cd</span> accounts
</code></pre>
<p>Next, add an empty <code>__init__.py</code> file to covert it into a Python package. Now, create a <code>views.py</code> file inside the package where you'll store all your routes related to user authentication.</p>
<pre><code class="lang-bash">touch __init__.py views.py
</code></pre>
<p>Add the following code inside the <code>views.py</code> file:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Blueprint

accounts_bp = Blueprint(<span class="hljs-string">"accounts"</span>, __name__)
</code></pre>
<p>In the above script, you have created a blueprint called <code>accounts_bp</code> for the <code>accounts</code> package.</p>
<p>Similarly, you can create a <code>core</code> package in the root directory, and add a <code>views.py</code> file.</p>
<pre><code class="lang-bash">mkdir core
<span class="hljs-built_in">cd</span> core
touch __init__.py views.py
</code></pre>
<p>Now, add the following code inside the <code>views.py</code> file:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Blueprint

core_bp = Blueprint(<span class="hljs-string">"core"</span>, __name__)
</code></pre>
<p>Note: If you're new to Flask Blueprints, make sure you go through <a target="_blank" href="https://ashutoshkrris.hashnode.dev/how-to-use-blueprints-to-organize-your-flask-apps">this tutorial</a> to learn more about how it works.</p>
<p>Now, your file structure should look like what you see below:</p>
<pre><code class="lang-bash">flask-two-factor-auth/
├── src/
│   ├── accounts/
│   │   ├── __init__.py
│   │   └── views.py
│   ├── core/
│   │   ├── __init__.py
│   │   └── views.py
│   └── __init__.py
├── .env
├── config.py
└── manage.py
</code></pre>
<h2 id="heading-how-to-create-a-user-model"><strong>How to Create a User Model</strong></h2>
<p>Let's create a <code>models.py</code> file inside the <code>accounts</code> package.</p>
<pre><code class="lang-bash">touch src/accounts/models.py
</code></pre>
<p>Inside the <code>models.py</code> file, add the following code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime

<span class="hljs-keyword">import</span> pyotp
<span class="hljs-keyword">from</span> flask_login <span class="hljs-keyword">import</span> UserMixin

<span class="hljs-keyword">from</span> src <span class="hljs-keyword">import</span> bcrypt, db
<span class="hljs-keyword">from</span> config <span class="hljs-keyword">import</span> Config


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span>(<span class="hljs-params">db.Model</span>):</span>

    __tablename__ = <span class="hljs-string">"users"</span>

    id = db.Column(db.Integer, primary_key=<span class="hljs-literal">True</span>)
    username = db.Column(db.String, unique=<span class="hljs-literal">True</span>, nullable=<span class="hljs-literal">False</span>)
    password = db.Column(db.String, nullable=<span class="hljs-literal">False</span>)
    created_at = db.Column(db.DateTime, nullable=<span class="hljs-literal">False</span>)
    is_two_factor_authentication_enabled = db.Column(
        db.Boolean, nullable=<span class="hljs-literal">False</span>, default=<span class="hljs-literal">False</span>)
    secret_token = db.Column(db.String, unique=<span class="hljs-literal">True</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, username, password</span>):</span>
        self.username = username
        self.password = bcrypt.generate_password_hash(password)
        self.created_at = datetime.now()
        self.secret_token = pyotp.random_base32()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_authentication_setup_uri</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> pyotp.totp.TOTP(self.secret_token).provisioning_uri(
            name=self.username, issuer_name=Config.APP_NAME)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">is_otp_valid</span>(<span class="hljs-params">self, user_otp</span>):</span>
        totp = pyotp.parse_uri(self.get_authentication_setup_uri())
        <span class="hljs-keyword">return</span> totp.verify(user_otp)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__repr__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"&lt;user <span class="hljs-subst">{self.username}</span>&gt;"</span>
</code></pre>
<p>In the above code, you created a <code>User</code> model by inheriting the <code>db.Model</code> class. The <code>User</code> model consists of the following fields:</p>
<ul>
<li><code>id</code>: stores the primary key for the <code>users</code> table</li>
<li><code>username</code>: stores the username of the user</li>
<li><code>password</code>: stores the hashed password of the user</li>
<li><code>created_at</code>: stores the timestamp when the user was created</li>
<li><code>is_two_factor_authentication_enabled</code>: boolean flag that stores whether the user has activated two-factor authentication. Default value is <code>False</code>.</li>
<li><code>secret_token</code>: stores a unique token generated for each user, essential for implementing two-factor authentication.</li>
</ul>
<p>The constructor initializes the <code>User</code> object upon instantiation by accepting <code>username</code> and <code>password</code> parameters. It hashes the provided password using <code>bcrypt.generate_password_hash(password)</code>, records the current timestamp as the <code>created_at</code> value, and generates a unique <code>secret_token</code> using <code>pyotp.random_base32()</code> for 2FA setup.</p>
<p>The <code>get_authentication_setup_uri()</code> method generates a setup URI used by authenticator apps like Google Authenticator. It constructs a URI containing the user's username and the application's name (<code>Config.APP_NAME</code>) necessary for setting up two-factor authentication. The basic format of the URI is:</p>
<pre><code class="lang-bash">otpauth://totp/Example:alice@google.com?secret=JBSWY3DPEHPK3PXP&amp;issuer=Example
</code></pre>
<p>where, alice@google.com is the username of the user and Example is the application's name.</p>
<p>Next up, the <code>is_otp_valid()</code> method verifies the one-time password (OTP) entered by the user during login. It parses the setup URI generated earlier, checks the validity of the provided OTP (<code>user_otp</code>), and returns <code>True</code> if the OTP matches, ensuring secure authentication.</p>
<p>Finally, the <code>__repr__</code> method provides a string representation of the <code>User</code> object, displaying the associated username when an instance of the class is printed or represented as a string.</p>
<h2 id="heading-how-to-add-flask-login"><strong>How to Add Flask-Login</strong></h2>
<p>The most important part of Flask-Login is the <code>LoginManager</code> class that lets your application and Flask-Login work together.</p>
<p>In the <code>src/__init__.py</code> file, add the following code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> decouple <span class="hljs-keyword">import</span> config
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask
<span class="hljs-keyword">from</span> flask_login <span class="hljs-keyword">import</span> LoginManager <span class="hljs-comment"># Add this line</span>
<span class="hljs-keyword">from</span> flask_migrate <span class="hljs-keyword">import</span> Migrate
<span class="hljs-keyword">from</span> flask_sqlalchemy <span class="hljs-keyword">import</span> SQLAlchemy

app = Flask(__name__)
app.config.from_object(config(<span class="hljs-string">"APP_SETTINGS"</span>))

login_manager = LoginManager() <span class="hljs-comment"># Add this line</span>
login_manager.init_app(app) <span class="hljs-comment"># Add this line</span>
db = SQLAlchemy(app)
migrate = Migrate(app, db)

<span class="hljs-comment"># Registering blueprints</span>
<span class="hljs-keyword">from</span> src.accounts.views <span class="hljs-keyword">import</span> accounts_bp
<span class="hljs-keyword">from</span> src.core.views <span class="hljs-keyword">import</span> core_bp

app.register_blueprint(accounts_bp)
app.register_blueprint(core_bp)
</code></pre>
<p>In the above script, we created and initialized the login manager in our app.</p>
<p>Next, we need to provide a <code>user_loader</code> callback. This callback is used to reload the user object from the user ID stored in the session. It should take the ID of a user, and return the corresponding user object.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> src.accounts.models <span class="hljs-keyword">import</span> User

<span class="hljs-meta">@login_manager.user_loader</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">load_user</span>(<span class="hljs-params">user_id</span>):</span>
    <span class="hljs-keyword">return</span> User.query.filter(User.id == int(user_id)).first()
</code></pre>
<p>The <code>User</code> model should implement the following properties and methods:</p>
<ul>
<li><code>is_authenticated</code>: This property returns True if the user is authenticated.</li>
<li><code>is_active</code>: This property returns True if this is an active user (the account is activated)</li>
<li><code>is_anonymous</code>: This property returns True if this is an anonymous user (actual users return False).</li>
<li><code>get_id()</code>: This method returns a string that uniquely identifies this user, and can be used to load the user from the <code>user_loader</code> callback.</li>
</ul>
<p>Now, we don't need to implement these explicitly. Instead, the Flask-Login provides a <code>UserMixin</code> class that contains the default implementations for all of these properties and methods. We just need to inherit it in the following way:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime

<span class="hljs-keyword">from</span> flask_login <span class="hljs-keyword">import</span> UserMixin <span class="hljs-comment"># Add this line</span>

<span class="hljs-keyword">from</span> src <span class="hljs-keyword">import</span> bcrypt, db


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span>(<span class="hljs-params">UserMixin, db.Model</span>):</span> <span class="hljs-comment"># Change this line</span>
    ....
</code></pre>
<p>We can also customize the default login process in the <code>src/__init__.py</code> file.</p>
<p>The name of the login view can be set as <code>LoginManager.login_view</code>. The value refers to the function name that will handle the login process.</p>
<pre><code class="lang-python">login_manager.login_view = <span class="hljs-string">"accounts.login"</span>
</code></pre>
<p>To customize the message category, set <code>LoginManager.login_message_category</code>:</p>
<pre><code class="lang-python">login_manager.login_message_category = <span class="hljs-string">"danger"</span>
</code></pre>
<h2 id="heading-how-to-add-templates-and-static-files"><strong>How to Add Templates and Static Files</strong></h2>
<p>Let's create a CSS file called <code>styles.css</code> inside the <code>src/static</code> folder:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.error</span> {
  <span class="hljs-attribute">color</span>: red;
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">5px</span>;
  <span class="hljs-attribute">text-align</span>: center;
}

<span class="hljs-selector-tag">a</span> {
  <span class="hljs-attribute">text-decoration</span>: none;
}
</code></pre>
<p>Let's also create the basic templates inside the <code>src/templates</code> folder. Create a <code>_base.html</code> file and add the following code:</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> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</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">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"utf-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Two Factor Authentication<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- meta --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"description"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">""</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"author"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">""</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width,initial-scale=1"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- styles --&gt;</span>
    <span class="hljs-comment">&lt;!-- CSS only --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</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">"{{url_for('static', filename="</span><span class="hljs-attr">styles.css</span>")}}"&gt;</span>
    {% block css %}{% endblock %}
  <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>

    {% include "navigation.html" %}

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container"</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">br</span>&gt;</span>

      <span class="hljs-comment">&lt;!-- messages --&gt;</span>
      {% with messages = get_flashed_messages(with_categories=true) %}
      {% if messages %}
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-4"</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-4"</span>&gt;</span>
          {% for category, message in messages %}
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"alert alert-{{ category }} alert-dismissible fade show"</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"alert"</span>&gt;</span>
           {{message}}
           <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn-close"</span> <span class="hljs-attr">data-bs-dismiss</span>=<span class="hljs-string">"alert"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Close"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          {% endfor %}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-4"</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">div</span>&gt;</span>
      {% endif %}
      {% endwith %}

      <span class="hljs-comment">&lt;!-- child template --&gt;</span>
      {% block content %}{% endblock %}

    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-comment">&lt;!-- scripts --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://code.jquery.com/jquery-3.7.1.min.js"</span> <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- JavaScript Bundle with Popper --&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"</span> <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</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">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"</span> <span class="hljs-attr">integrity</span>=<span class="hljs-string">"sha384-BBtl+eGJRgqQAUMxJ7pMwbEyER4l1g+O15P+16Ep7Q9Q+zqX6gSbd85u4mG4QzX+"</span> <span class="hljs-attr">crossorigin</span>=<span class="hljs-string">"anonymous"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    {% block js %}{% endblock %}
  <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>The <code>_base.html</code> is the parent HTML file that will be inherited by the other templates. We have added Bootstrap 5 support in the above file. We are also making use of Flask Flashes to show Bootstrap alerts in the app.</p>
<p>Let's also create a <code>navigation.html</code> file that contains the navbar of the app:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Navigation --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar bg-dark navbar-expand-lg bg-body-tertiary p-3"</span> <span class="hljs-attr">data-bs-theme</span>=<span class="hljs-string">"dark"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"container-fluid"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-brand"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ url_for('core.home') }}"</span>&gt;</span>Two-Factor Authentication App<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-toggler"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">data-bs-toggle</span>=<span class="hljs-string">"collapse"</span> <span class="hljs-attr">data-bs-target</span>=<span class="hljs-string">"#navbarSupportedContent"</span> <span class="hljs-attr">aria-controls</span>=<span class="hljs-string">"navbarSupportedContent"</span> <span class="hljs-attr">aria-expanded</span>=<span class="hljs-string">"false"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Toggle navigation"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navbar-toggler-icon"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <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">"collapse navbar-collapse"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"navbarSupportedContent"</span>&gt;</span>
      {% if current_user.is_authenticated %}
      <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ url_for('accounts.logout') }}"</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-danger me-2"</span>&gt;</span>Logout<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      {% endif %}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</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">nav</span>&gt;</span>
</code></pre>
<p>Note that we have not yet created the views used above.</p>
<h2 id="heading-how-to-create-the-homepage"><strong>How to Create the Homepage</strong></h2>
<p>In this section, we'll first create a view function for the homepage inside the <code>core/views.py</code> file. Add the following code there:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Blueprint, render_template
<span class="hljs-keyword">from</span> flask_login <span class="hljs-keyword">import</span> login_required

core_bp = Blueprint(<span class="hljs-string">"core"</span>, __name__)


<span class="hljs-meta">@core_bp.route("/")</span>
<span class="hljs-meta">@login_required</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">home</span>():</span>
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">"core/index.html"</span>)
</code></pre>
<p>Notice that we have used the blueprint to add the route. We also added a <code>@login_required</code> middleware to prevent access for unauthenticated users.</p>
<p>Next, let's create an <code>index.html</code> file inside the <code>templates/core</code> folder, and add the following code:</p>
<pre><code class="lang-html">{% extends "_base.html" %}
{% block content %}

<span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-center"</span>&gt;</span>Welcome {{current_user.username}}!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

{% endblock %}
</code></pre>
<p>The HTML page will just have a welcome message for authenticated users.</p>
<p>Your file structure as of now should look like below:</p>
<pre><code class="lang-bash">flask-two-factor-auth/
├── src/
│   ├── accounts/
│   │   ├── __init__.py
│   │   └── views.py
│   ├── core/
│   │   ├── __init__.py
│   │   └── views.py
│   ├── static/
│   │   └── styles.css
│   ├── templates/
│   │   ├── core/
│   │   │   └── index.html
│   │   ├── _base.html
│   │   └── navigation.html
│   └── __init__.py
├── .env
├── config.py
└── manage.py
</code></pre>
<h2 id="heading-how-to-implement-user-registration"><strong>How to Implement User Registration</strong></h2>
<p>First of all, we'll create a registration form using Flask-WTF. Create a <code>forms.py</code> file inside the <code>accounts</code> package and add the following code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask_wtf <span class="hljs-keyword">import</span> FlaskForm
<span class="hljs-keyword">from</span> wtforms <span class="hljs-keyword">import</span> EmailField, PasswordField
<span class="hljs-keyword">from</span> wtforms.validators <span class="hljs-keyword">import</span> DataRequired, Email, EqualTo, Length

<span class="hljs-keyword">from</span> src.accounts.models <span class="hljs-keyword">import</span> User


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RegisterForm</span>(<span class="hljs-params">FlaskForm</span>):</span>
    username = StringField(
        <span class="hljs-string">"Username"</span>, validators=[DataRequired(), Length(min=<span class="hljs-number">6</span>, max=<span class="hljs-number">40</span>)]
    )
    password = PasswordField(
        <span class="hljs-string">"Password"</span>, validators=[DataRequired(), Length(min=<span class="hljs-number">6</span>, max=<span class="hljs-number">25</span>)]
    )
    confirm = PasswordField(
        <span class="hljs-string">"Repeat password"</span>,
        validators=[
            DataRequired(),
            EqualTo(<span class="hljs-string">"password"</span>, message=<span class="hljs-string">"Passwords must match."</span>),
        ],
    )

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">validate</span>(<span class="hljs-params">self, extra_validators</span>):</span>
        initial_validation = super(RegisterForm, self).validate(extra_validators)
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> initial_validation:
            <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
        user = User.query.filter_by(username=self.username.data).first()
        <span class="hljs-keyword">if</span> user:
            self.username.errors.append(<span class="hljs-string">"Username already registered"</span>)
            <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
        <span class="hljs-keyword">if</span> self.password.data != self.confirm.data:
            self.password.errors.append(<span class="hljs-string">"Passwords must match"</span>)
            <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
        <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
</code></pre>
<p>The <code>RegisterForm</code> extends the <code>FlaskForm</code> class and contains three fields – <code>username</code>, <code>password</code>, and <code>confirm</code>. We have added different validators such as <code>DataRequired</code>, <code>Length</code>, <code>Email</code>, and <code>EqualTo</code> to the respective fields.</p>
<p>We also defined a <code>validate()</code> method which is automatically called when the form is submitted.</p>
<p>Inside the method, we first perform the initial validation provided by FlaskForm. If that is successful, we perform our custom validation such as checking whether user is already registered, and matching the password with the confirmed password. If there are any errors, we append the error message in the respective fields.</p>
<p>Now, let's use this form inside the HTML file. Create an <code>accounts</code> directory inside the <code>templates</code> folder and add a new file called <code>register.html</code> inside it. Add the following code:</p>
<pre><code class="lang-html">{% extends "_base.html" %}

{% block content %}

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-4"</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-signin w-100 m-auto"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"form"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">""</span>&gt;</span>
        {{ form.csrf_token }}
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h3 mb-3 fw-normal text-center"</span>&gt;</span>Please register<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-floating"</span>&gt;</span>
          {{ form.username(placeholder="username", class="form-control mb-2") }}
          {{ form.username.label }}
            {% if form.username.errors %}
              {% for error in form.username.errors %}
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"alert alert-danger"</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"alert"</span>&gt;</span>
                  {{ error }}
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              {% endfor %}
            {% endif %}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-floating"</span>&gt;</span>
          {{ form.password(placeholder="password", class="form-control mb-2") }}
          {{ form.password.label }}
            {% if form.password.errors %}
              {% for error in form.password.errors %}
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"alert alert-danger"</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"alert"</span>&gt;</span>
                  {{ error }}
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              {% endfor %}
            {% endif %}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-floating"</span>&gt;</span>
          {{ form.confirm(placeholder="Confirm Password", class="form-control mb-2") }}
          {{ form.confirm.label }}
            {% if form.confirm.errors %}
              {% for error in form.confirm.errors %}
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"alert alert-danger"</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"alert"</span>&gt;</span>
                  {{ error }}
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              {% endfor %}
            {% endif %}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-100 btn btn-lg btn-primary"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Sign up<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-center mt-3"</span>&gt;</span>Already registered? <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ url_for('accounts.login') }}"</span>&gt;</span>Login now<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-4"</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">div</span>&gt;</span>

{% endblock %}
</code></pre>
<p>In the above Jinja template, we make use of the form that we created, and add relevant error handling logic checks for validation errors in each field. Users can submit the form by clicking the "Sign up" button, and a link below the form allows already registered users to navigate to the login page for authentication.</p>
<p>Next, let's use this form in the <code>views.py</code> to create a function to handle the registration process.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> .forms <span class="hljs-keyword">import</span> RegisterForm
<span class="hljs-keyword">from</span> src.accounts.models <span class="hljs-keyword">import</span> User
<span class="hljs-keyword">from</span> src <span class="hljs-keyword">import</span> db, bcrypt
<span class="hljs-keyword">from</span> flask_login <span class="hljs-keyword">import</span> current_user
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Blueprint, flash, redirect, render_template, request, url_for

accounts_bp = Blueprint(<span class="hljs-string">"accounts"</span>, __name__)

HOME_URL = <span class="hljs-string">"core.home"</span>
SETUP_2FA_URL = <span class="hljs-string">"accounts.setup_two_factor_auth"</span>
VERIFY_2FA_URL = <span class="hljs-string">"accounts.verify_two_factor_auth"</span>

<span class="hljs-meta">@accounts_bp.route("/register", methods=["GET", "POST"])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">register</span>():</span>
    <span class="hljs-keyword">if</span> current_user.is_authenticated:
        <span class="hljs-keyword">if</span> current_user.is_two_factor_authentication_enabled:
            flash(<span class="hljs-string">"You are already registered."</span>, <span class="hljs-string">"info"</span>)
            <span class="hljs-keyword">return</span> redirect(url_for(HOME_URL))
        <span class="hljs-keyword">else</span>:
            flash(<span class="hljs-string">"You have not enabled 2-Factor Authentication. Please enable first to login."</span>, <span class="hljs-string">"info"</span>)
            <span class="hljs-keyword">return</span> redirect(url_for(SETUP_2FA_URL))
    form = RegisterForm(request.form)
    <span class="hljs-keyword">if</span> form.validate_on_submit():
        <span class="hljs-keyword">try</span>:
            user = User(username=form.username.data, password=form.password.data)
            db.session.add(user)
            db.session.commit()

            login_user(user)
            flash(<span class="hljs-string">"You are registered. You have to enable 2-Factor Authentication first to login."</span>, <span class="hljs-string">"success"</span>)

            <span class="hljs-keyword">return</span> redirect(url_for(SETUP_2FA_URL))
        <span class="hljs-keyword">except</span> Exception:
            db.session.rollback()
            flash(<span class="hljs-string">"Registration failed. Please try again."</span>, <span class="hljs-string">"danger"</span>)

    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">"accounts/register.html"</span>, form=form)
</code></pre>
<p>The route begins by checking if the current user is already authenticated. If so, it verifies whether 2FA is enabled for the user. If 2FA is already enabled, a message informs the user that they're already registered, redirecting them to the home URL. However, if the user is authenticated but 2FA is not enabled, a flash message prompts the user to enable 2FA first before logging in, redirecting them to the 2FA setup URL.</p>
<p>If the user is not authenticated or has not yet registered 2FA, the code initializes a registration form and proceeds to validate the form data on submission. Upon successful form validation, we create a new <code>User</code> object with the provided username and password and save it to the database.</p>
<p>Upon successful user registration, the newly registered user is logged in. A success message flashes, notifying the user of successful registration and prompting them to enable 2FA before logging in. Subsequently, the user is redirected to the 2FA setup URL to enable 2FA.</p>
<h2 id="heading-how-to-implement-user-login"><strong>How to Implement User Login</strong></h2>
<p>First, let's create a login form in the <code>accounts/forms.py</code> file:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LoginForm</span>(<span class="hljs-params">FlaskForm</span>):</span>
    username = StringField(<span class="hljs-string">"Username"</span>, validators=[DataRequired()])
    password = PasswordField(<span class="hljs-string">"Password"</span>, validators=[DataRequired()])
</code></pre>
<p>The form is similar to the registration form but it has only two fields – <code>username</code> and <code>password</code>.</p>
<p>Now, let's use this form inside new HTML file called <code>login.html</code> created inside the <code>templates/accounts</code> directory. Add the following code:</p>
<pre><code class="lang-html">{% extends "_base.html" %}

{% block content %}

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-4"</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-signin w-100 m-auto"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"form"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">""</span>&gt;</span>
        {{ form.csrf_token }}
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h3 mb-3 fw-normal text-center"</span>&gt;</span>Please sign in<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-floating"</span>&gt;</span>
          {{ form.username(placeholder="username", class="form-control mb-2") }}
          {{ form.username.label }}
            {% if form.username.errors %}
              {% for error in form.username.errors %}
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"alert alert-danger"</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"alert"</span>&gt;</span>
                {{ error }}
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              {% endfor %}
            {% endif %}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-floating"</span>&gt;</span>
          {{ form.password(placeholder="password", class="form-control mb-2") }}
          {{ form.password.label }}
            {% if form.password.errors %}
              {% for error in form.password.errors %}
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"alert alert-danger"</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"alert"</span>&gt;</span>
                  {{ error }}
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              {% endfor %}
            {% endif %}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-100 btn btn-lg btn-primary"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Sign in<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-center mt-3"</span>&gt;</span>New User? <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ url_for('accounts.register') }}"</span>&gt;</span>Register now<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-4"</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">div</span>&gt;</span>

{% endblock %}
</code></pre>
<p>The above HTML file is also similar to the <code>register.html</code> file but with just two fields for the username and password.</p>
<p>Next, let's create a view function to handle the login process inside the <code>accounts/views.py</code> file:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> .forms <span class="hljs-keyword">import</span> LoginForm, RegisterForm

<span class="hljs-meta">@accounts_bp.route("/login", methods=["GET", "POST"])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">login</span>():</span>
    <span class="hljs-keyword">if</span> current_user.is_authenticated:
        <span class="hljs-keyword">if</span> current_user.is_two_factor_authentication_enabled:
            flash(<span class="hljs-string">"You are already logged in."</span>, <span class="hljs-string">"info"</span>)
            <span class="hljs-keyword">return</span> redirect(url_for(HOME_URL))
        <span class="hljs-keyword">else</span>:
            flash(<span class="hljs-string">"You have not enabled 2-Factor Authentication. Please enable first to login."</span>, <span class="hljs-string">"info"</span>)
            <span class="hljs-keyword">return</span> redirect(url_for(SETUP_2FA_URL))

    form = LoginForm(request.form)
    <span class="hljs-keyword">if</span> form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        <span class="hljs-keyword">if</span> user <span class="hljs-keyword">and</span> bcrypt.check_password_hash(user.password, request.form[<span class="hljs-string">"password"</span>]):
            login_user(user)
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> current_user.is_two_factor_authentication_enabled:
                flash(
                    <span class="hljs-string">"You have not enabled 2-Factor Authentication. Please enable first to login."</span>, <span class="hljs-string">"info"</span>)
                <span class="hljs-keyword">return</span> redirect(url_for(SETUP_2FA_URL))
            <span class="hljs-keyword">return</span> redirect(url_for(VERIFY_2FA_URL))
        <span class="hljs-keyword">elif</span> <span class="hljs-keyword">not</span> user:
            flash(<span class="hljs-string">"You are not registered. Please register."</span>, <span class="hljs-string">"danger"</span>)
        <span class="hljs-keyword">else</span>:
            flash(<span class="hljs-string">"Invalid username and/or password."</span>, <span class="hljs-string">"danger"</span>)
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">"accounts/login.html"</span>, form=form)
</code></pre>
<p>The route starts by checking if the current user is already authenticated. If the user is authenticated and 2FA is enabled, a message informs the user they're already logged in, redirecting them to the home URL. If the user is authenticated but 2FA isn't enabled, a flash message prompts the user to enable 2FA before logging in, redirecting them to the 2FA setup URL.</p>
<p>If the user isn't authenticated, the code initializes a login form and validates the form data upon submission. Upon successful validation, it queries the database to find a user matching the provided username. If the user exists and the password matches the hashed password stored in the database, the user is logged in.</p>
<p>Additionally, if 2FA isn't enabled for the current user after successful login, a flash message prompts the user to enable 2FA before proceeding, redirecting them to the 2FA setup URL. If the login is successful and 2FA is enabled, the user is redirected to the 2FA verification URL.</p>
<p>If the user isn't registered, a flash message informs them to register. If there's a mismatch in the provided username or password, another flash message notifies the user of invalid credentials.</p>
<h2 id="heading-how-to-log-out-the-users">How to Log Out the Users</h2>
<p>Logging out the user is a very simple process. You just need to create a view function for it inside the <code>accounts/views.py</code> file:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask_login <span class="hljs-keyword">import</span> login_required, login_user, logout_user


<span class="hljs-meta">@accounts_bp.route("/logout")</span>
<span class="hljs-meta">@login_required</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">logout</span>():</span>
    logout_user()
    flash(<span class="hljs-string">"You were logged out."</span>, <span class="hljs-string">"success"</span>)
    <span class="hljs-keyword">return</span> redirect(url_for(<span class="hljs-string">"accounts.login"</span>))
</code></pre>
<p>The <code>Flask-Login</code> library contains a <code>logout_user</code> method that removes the user from the session. We used the <code>@login_required</code> decorator so that only authenticated users can logout.</p>
<h2 id="heading-how-to-add-the-setup-2fa-page">How to Add the Setup 2FA Page</h2>
<p>Up until now, we have been redirecting the users to the setup 2FA page whenever the 2FA is not enabled in their accounts, but we haven't implemented it yet. Let's do that in this section.</p>
<p>Let's start with the route for the page:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> src.utils <span class="hljs-keyword">import</span> get_b64encoded_qr_image

<span class="hljs-meta">@accounts_bp.route("/setup-2fa")</span>
<span class="hljs-meta">@login_required</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">setup_two_factor_auth</span>():</span>
    secret = current_user.secret_token
    uri = current_user.get_authentication_setup_uri()
    base64_qr_image = get_b64encoded_qr_image(uri)
    <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">"accounts/setup-2fa.html"</span>, secret=secret, qr_image=base64_qr_image)
</code></pre>
<p>The route, created inside <code>accounts/views.py</code>, ensures that only authenticated users can access it using the <code>@login_required</code> decorator. </p>
<p>Upon accessing this route, the function retrieves the current user's <code>secret_token</code> for 2FA setup and generates a URI through <code>current_user.get_authentication_setup_uri()</code> to configure an authenticator app like Google Authenticator. </p>
<p>It also uses <code>get_b64encoded_qr_image(uri)</code> to obtain a Base64-encoded QR code image representing this setup URI. We will define it below. </p>
<p>Finally, it renders the <code>setup-2fa.html</code> template, passing the user's <code>secret_token</code> and the Base64-encoded QR image to the template for users to scan it.</p>
<p>Next, create a <code>utils.py</code> file in the <code>src</code> directory and add the following code to <a target="_blank" href="https://blog.ashutoshkrris.in/5-quick-python-projects#heading-qr-codes-in-python">generate the QR</a>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> io <span class="hljs-keyword">import</span> BytesIO
<span class="hljs-keyword">import</span> qrcode
<span class="hljs-keyword">from</span> base64 <span class="hljs-keyword">import</span> b64encode


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_b64encoded_qr_image</span>(<span class="hljs-params">data</span>):</span>
    print(data)
    qr = qrcode.QRCode(version=<span class="hljs-number">1</span>, box_size=<span class="hljs-number">10</span>, border=<span class="hljs-number">5</span>)
    qr.add_data(data)
    qr.make(fit=<span class="hljs-literal">True</span>)
    img = qr.make_image(fill_color=<span class="hljs-string">'black'</span>, back_color=<span class="hljs-string">'white'</span>)
    buffered = BytesIO()
    img.save(buffered)
    <span class="hljs-keyword">return</span> b64encode(buffered.getvalue()).decode(<span class="hljs-string">"utf-8"</span>)
</code></pre>
<p>Remember the <code>qrcode</code> library we installed in the beginning of the tutorial? This is where we're going to use it. </p>
<p>Upon receiving <code>data</code> as input, representing the content to be embedded within the QR code, the function initializes a QRCode object using the <code>qrcode</code> library. It adds the provided data to this QR code instance and generates the QR code. The code then converts this QR code into an image representation. </p>
<p>Using a BytesIO object, it stores this image in memory. The function proceeds to encode the content of this in-memory buffer, representing the QR code image, into Base64 format. Finally, it returns this Base64-encoded string, encapsulating the QR code image, ready for transmission or display in various applications.</p>
<p>Next, let's create the <code>setup-2fa.html</code> page inside the <code>templates/accounts</code> folder, and add the following content:</p>
<pre><code class="lang-html">{% extends "_base.html" %}

{% block content %}

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-4"</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-signin w-100 m-auto"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"form"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h5</span>&gt;</span>Instructions!<span class="hljs-tag">&lt;/<span class="hljs-name">h5</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">li</span>&gt;</span>Download <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&amp;hl=en&amp;gl=US"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>Google Authenticator<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span> on your mobile.<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Set up a new authenticator.<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Once you have scanned the QR, please click <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ url_for('accounts.verify_two_factor_auth') }}"</span>&gt;</span>here.<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> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-center"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"data:image/png;base64, {{ qr_image }}"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Secret Token"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"width:200px;height:200px"</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-group"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"secret"</span>&gt;</span>Secret Token<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-control"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"secret"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"{{ secret }}"</span> <span class="hljs-attr">readonly</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-center mt-2"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"btn btn-primary"</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"copySecret()"</span>&gt;</span>
            Copy Secret
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</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">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mt-4 text-center"</span>&gt;</span>
          Once you have scanned the QR, please click <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"{{ url_for('accounts.verify_two_factor_auth') }}"</span>&gt;</span>here<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>.
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-4"</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">div</span>&gt;</span>

{% endblock %}

{% block js %}
<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">copySecret</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">var</span> copyText = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"secret"</span>);
    copyText.select();
    copyText.setSelectionRange(<span class="hljs-number">0</span>, <span class="hljs-number">99999</span>); <span class="hljs-comment">/*For mobile devices*/</span>
    <span class="hljs-built_in">document</span>.execCommand(<span class="hljs-string">"copy"</span>);
    alert(<span class="hljs-string">"Successfully copied TOTP secret token!"</span>);
  }
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
{% endblock %}
</code></pre>
<p>We add some instructions in the page for the users to follow. These instructions provide clear steps for users to enable 2FA: directing them to download the Google Authenticator app via a link, guiding the setup process within the app, and prompting users to proceed by clicking a link after scanning the displayed QR code.</p>
<p>Displaying the QR code is central to the setup process. The template embeds the QR code image using an <code>&lt;img&gt;</code> tag with its source set to a Base64-encoded string (<code>{{ qr_image }}</code>). This image represents the secret key essential for 2FA setup. </p>
<p>We also show the secret key in read-only mode, allowing users to view the key without being able to modify it. We have added a copy button to make it easier for the users to copy the key.</p>
<p>Moreover, we have added a link to the 2FA verification page guiding users to proceed with the setup process after scanning the QR code. We will implement this functionality in the next section.</p>
<p>Here's how your page looks right now:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Screenshot-2023-11-26-010925.png" alt="Image" width="600" height="400" loading="lazy">
<em>2FA Setup Page</em></p>
<h2 id="heading-how-to-add-a-2fa-verification-page">How to Add a 2FA Verification Page</h2>
<p>In this section, let's implement the 2FA verification. To start with, we will require an OTP form where users can enter their OTP. Add the following content in the <code>accounts/forms.py</code> file:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TwoFactorForm</span>(<span class="hljs-params">FlaskForm</span>):</span>
    otp = StringField(<span class="hljs-string">'Enter OTP'</span>, validators=[
                      InputRequired(), Length(min=<span class="hljs-number">6</span>, max=<span class="hljs-number">6</span>)])
</code></pre>
<p>The <code>TwoFactorForm</code> contains just one field (<code>otp</code>) to get the OTP from the users.</p>
<p>Now, let's use this form in the <code>verify-2fa.html</code> file inside the <code>templates/accounts</code> folder:</p>
<pre><code class="lang-html">{% extends "_base.html" %}

{% block content %}

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"row"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-4"</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-4"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-signin w-100 m-auto"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"form"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">""</span>&gt;</span>
        {{ form.csrf_token }}
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"h3 mb-3 fw-normal text-center"</span>&gt;</span>Enter OTP<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"form-floating"</span>&gt;</span>
          {{ form.otp(placeholder="OTP", class="form-control mb-2") }}
          {{ form.otp.label }}
            {% if form.otp.errors %}
              {% for error in form.otp.errors %}
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"alert alert-danger"</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"alert"</span>&gt;</span>
                {{ error }}
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              {% endfor %}
            {% endif %}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-100 btn btn-lg btn-primary"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Verify<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</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">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-md-4"</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">div</span>&gt;</span>

{% endblock %}
</code></pre>
<p>The Jinja template essentially contains a form with one field for OTP and a verify button. </p>
<p>Let's create the route which handles the submission of this form inside the <code>accounts/views.py</code> file:</p>
<pre><code class="lang-python"><span class="hljs-meta">@accounts_bp.route("/verify-2fa", methods=["GET", "POST"])</span>
<span class="hljs-meta">@login_required</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">verify_two_factor_auth</span>():</span>
    form = TwoFactorForm(request.form)
    <span class="hljs-keyword">if</span> form.validate_on_submit():
        <span class="hljs-keyword">if</span> current_user.is_otp_valid(form.otp.data):
            <span class="hljs-keyword">if</span> current_user.is_two_factor_authentication_enabled:
                flash(<span class="hljs-string">"2FA verification successful. You are logged in!"</span>, <span class="hljs-string">"success"</span>)
                <span class="hljs-keyword">return</span> redirect(url_for(HOME_URL))
            <span class="hljs-keyword">else</span>:
                <span class="hljs-keyword">try</span>:
                    current_user.is_two_factor_authentication_enabled = <span class="hljs-literal">True</span>
                    db.session.commit()
                    flash(<span class="hljs-string">"2FA setup successful. You are logged in!"</span>, <span class="hljs-string">"success"</span>)
                    <span class="hljs-keyword">return</span> redirect(url_for(HOME_URL))
                <span class="hljs-keyword">except</span> Exception:
                    db.session.rollback()
                    flash(<span class="hljs-string">"2FA setup failed. Please try again."</span>, <span class="hljs-string">"danger"</span>)
                    <span class="hljs-keyword">return</span> redirect(url_for(VERIFY_2FA_URL))
        <span class="hljs-keyword">else</span>:
            flash(<span class="hljs-string">"Invalid OTP. Please try again."</span>, <span class="hljs-string">"danger"</span>)
            <span class="hljs-keyword">return</span> redirect(url_for(VERIFY_2FA_URL))
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> current_user.is_two_factor_authentication_enabled:
            flash(
                <span class="hljs-string">"You have not enabled 2-Factor Authentication. Please enable it first."</span>, <span class="hljs-string">"info"</span>)
        <span class="hljs-keyword">return</span> render_template(<span class="hljs-string">"accounts/verify-2fa.html"</span>, form=form)
</code></pre>
<p>The route starts by initializing a form (<code>TwoFactorForm</code>) meant for 2FA verification using the data obtained from the request. Upon form submission, the code proceeds with several conditional checks to validate the OTP entered by the user.</p>
<p>Once the form has been successfully submitted and validated, the code verifies the authenticity of the OTP using <code>current_user.is_otp_valid(form.otp.data)</code>, which checks if the entered OTP is valid for the current user. If the OTP is valid, the code executes the following logic:</p>
<ul>
<li>If the provided OTP is valid and 2FA is already enabled for the user, a success message is flashed indicating successful 2FA verification, and the user is redirected to the home URL.</li>
<li>If the OTP is valid but 2FA isn't enabled for the user, it attempts to enable 2FA for that user. Upon successful activation, a success message flashes, and the user is redirected to the home URL.</li>
</ul>
<p>Furthermore, if the OTP entered by the user is invalid, the code flashes an error message indicating an invalid OTP and redirects the user back to the 2FA verification URL to retry the verification process.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/11/Screenshot-2023-11-26-011107.png" alt="Image" width="600" height="400" loading="lazy">
<em>2FA Verification Page</em></p>
<p>With this, we have completed the implementation of all the features! 🎉</p>
<h2 id="heading-how-to-run-the-completed-app-for-the-first-time"><strong>How to Run the Completed App for the First Time</strong></h2>
<p>Now that our application is ready, you can first migrate the database, and then run the app.</p>
<p>To initialize the database (create a migration repository), use the command:</p>
<pre><code class="lang-bash">flask db init
</code></pre>
<p>To migrate the database changes, use the command:</p>
<pre><code class="lang-bash">flask db migrate
</code></pre>
<p>To apply the migrations, use the command:</p>
<pre><code class="lang-bash">flask db upgrade
</code></pre>
<p>Since this is the first time we're running our app, you'll need to run all the above commands. Later, whenever you make changes to the database, you'll just need to run the last two commands.</p>
<p>After that, you can run your application using the command:</p>
<pre><code>python manage.py run
</code></pre><p>Since we have completed the development, here's how your file structure should look like:</p>
<pre><code class="lang-bash">flask-two-factor-auth/
├── migrations/
├── src/
│   ├── accounts/
│   │   ├── __init__.py
│   │   ├── forms.py
│   │   ├── models.py
│   │   └── views.py
│   ├── core/
│   │   ├── __init__.py
│   │   └── views.py
│   ├── static/
│   │   └── styles.css
│   ├── templates/
│   │   ├── accounts/
│   │   │   ├── login.html
│   │   │   ├── register.html
│   │   │   ├── setup-2fa.html
│   │   │   └── verify-2fa.html
│   │   ├── core/
│   │   │   └── index.html
│   │   ├── _base.html
│   │   └── navigation.html
│   ├── __init__.py
│   └── utils.py
├── .env
├── config.py
└── manage.py
</code></pre>
<h2 id="heading-wrapping-up"><strong>Wrapping up</strong></h2>
<p>In this tutorial, you learned how to set up two-factor authentication in your Flask app using PyOTP.</p>
<p>Here's the link to the <a target="_blank" href="https://github.com/ashutoshkrris/Flask-Two-Factor-Authentication">GitHub repository</a>. Feel free to check it out whenever you're stuck.</p>
<p>Here are some other tutorials I wrote about authentication, email verification, and OTPs that you might enjoy:</p>
<ul>
<li><a target="_blank" href="https://blog.ashutoshkrris.in/how-to-set-up-basic-user-authentication-in-a-flask-app">How to Set Up Basic User Authentication in a Flask App</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/how-to-setup-user-authentication-in-flask/">How to Set Up Email Verification in a Flask App</a></li>
<li><a target="_blank" href="https://blog.ashutoshkrris.in/how-to-generate-otps-using-pyotp-in-python">How To Generate OTPs Using PyOTP in Python</a></li>
</ul>
<p>Thank you for reading. I hope you found this article useful. You can follow me on <a target="_blank" href="https://twitter.com/ashutoshkrris">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Automate Data Exports and Email Reports with Python – a Step-by-Step Guide ]]>
                </title>
                <description>
                    <![CDATA[ In today's data-driven world, automation is key to streamlining tasks and saving time. In this beginner-friendly tutorial, I'll walk you through the process of automating data exports from a PostgreSQL database and sending them as an email attachment... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/automate-data-exports-email-reports-with-python/</link>
                <guid isPermaLink="false">66ba0e68d14c87384322b68d</guid>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ excel ]]>
                    </category>
                
                    <category>
                        <![CDATA[ postgres ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Mon, 30 Oct 2023 16:08:37 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/10/report-automation.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In today's data-driven world, automation is key to streamlining tasks and saving time. In this beginner-friendly tutorial, I'll walk you through the process of automating data exports from a PostgreSQL database and sending them as an email attachment using Python. </p>
<p>This step-by-step guide will help you grasp the fundamentals of working with databases, data manipulation, and email communication, all while automating these processes with a Python script.</p>
<h2 id="heading-business-context">Business Context</h2>
<p>Imagine you're a part of an organization where your managers expect a weekly report filled with valuable insights. But creating this report is far from a straightforward task. </p>
<p>To get the information you need, you have to manually run ten different database queries, gather the results, and then meticulously compile them into an Excel spreadsheet. It's a time-consuming and error-prone process that can leave you exhausted.</p>
<p>In this scenario, wouldn't it be a game-changer if Python could take the reins and handle this entire process for you? </p>
<p>Picture this: Every week, without any manual intervention, Python seamlessly extracts the required data, compiles it into a neat Excel sheet, and even sends it off to your managers like clockwork. </p>
<p>This tutorial will help you learn how to do this. I'll walk you through the steps to automate this process, making your weekly or monthly reporting a breeze, and freeing you up to focus on more critical tasks.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-your-virtual-environment">How to Set Up Your Virtual Environment</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-your-sample-database">How to Set Up Your Sample Database</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-logging-and-environment-variables">How to Set Up Logging and Environment Variables</a></li>
<li><a class="post-section-overview" href="#heading-how-to-extract-the-data-from-the-database">How to Extract the Data From the Database</a></li>
<li><a class="post-section-overview" href="#heading-how-to-structure-the-booking-data-with-the-bookinginfo-class">How to Structure the Booking Data with the <code>BookingInfo</code> Class</a></li>
<li><a class="post-section-overview" href="#heading-how-to-convert-the-data-into-an-excel-sheet">How to Convert the Data into an Excel Sheet</a></li>
<li><a class="post-section-overview" href="#heading-how-to-combine-the-functionalities">How to Combine the Functionalities</a></li>
<li><a class="post-section-overview" href="#heading-how-to-send-an-email-with-the-bookings-data-report">How to Send an Email with the Bookings Data Report</a></li>
<li><a class="post-section-overview" href="#heading-how-to-test-the-flow">How to Test the Flow</a></li>
<li><a class="post-section-overview" href="#heading-how-to-schedule-the-application">How to Schedule the Application</a></li>
<li><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></li>
</ol>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you get started, make sure you have the following:</p>
<ol>
<li>Python installed on your computer. You can download Python from <a target="_blank" href="https://www.python.org/downloads/">Python.org</a>.</li>
<li>Basic knowledge of the Python programming language</li>
<li>Familiarity with <a target="_blank" href="https://blog.ashutoshkrris.in/how-to-send-emails-using-python">sending emails in Python</a></li>
<li>PostgreSQL installed on your computer. You can download PostgreSQL from <a target="_blank" href="https://www.postgresql.org/download/">here</a>.</li>
</ol>
<h2 id="heading-how-to-set-up-your-virtual-environment">How to Set Up Your Virtual Environment</h2>
<p>Before you start coding, you'll need to make sure you have all the necessary tools and libraries installed. </p>
<p>To ensure that you have a clean and isolated environment, you'll <a target="_blank" href="https://www.freecodecamp.org/news/how-to-setup-virtual-environments-in-python/">create a virtual environment</a> using <code>venv</code>.</p>
<p>Create a project directory and navigate to it in the terminal:</p>
<pre><code class="lang-bash">mkdir report-automation
<span class="hljs-built_in">cd</span> report-automation
</code></pre>
<p>Create a virtual environment named <code>env</code> using the following command:</p>
<pre><code class="lang-bash">python -m venv env
</code></pre>
<p>Python now ships with the pre-installed <code>venv</code> library to create virtual environments.</p>
<p>Activate the virtual environment like this:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">source</span> env/bin/activate
</code></pre>
<p>Note: if you're on Windows, you'll need to use <code>source env/Scripts/activate</code> to activate the environment.</p>
<p>You should see <code>(env)</code> in your terminal prompt, indicating that the virtual environment has been activated.</p>
<h3 id="heading-how-to-install-the-required-libraries">How to Install the Required Libraries</h3>
<p>Now that you've created the virtual environment, you can install the following libraries:</p>
<ul>
<li><code>psycopg2</code>: Python adapter for PostgreSQL, enabling Python applications to interact with PostgreSQL databases.</li>
<li><code>pandas</code>: A versatile data manipulation and analysis library for Python, ideal for working with structured data.</li>
<li><code>xlsxwriter</code>: Python module for creating and formatting Excel (XLSX) files, useful for generating reports and spreadsheets.</li>
</ul>
<p>To install the libraries, run the following command:</p>
<pre><code class="lang-bash">pip install psycopg2 pandas xlsxwriter
</code></pre>
<h2 id="heading-how-to-set-up-your-sample-database">How to Set Up Your Sample Database</h2>
<p>In this section, I will guide you through setting up a demo database named "airlines" that we'll use throughout this tutorial. The database includes three tables: <code>bookings</code>, <code>flights</code>, and <code>airports_data</code>. </p>
<p>I will provide you with an SQL script file named <code>airlines_db.sql</code> that creates the database and populates it with sample data. To set up the database, you will need PostgreSQL installed on your system.</p>
<h3 id="heading-download-and-install-the-database">Download and Install the Database</h3>
<ol>
<li>Download the SQL script file "airlines_db.sql" from <a target="_blank" href="https://drive.google.com/file/d/1CPo4ZC8dmuyCetEwpyDa6pfKnpbiqyO3/view?usp=sharing">here</a>.</li>
<li>Open your terminal or command prompt.</li>
<li>Use the following command to install the database. Make sure you have the PostgreSQL command-line tools installed and that you can access the <code>psql</code> command. Replace <code>postgres</code> with your PostgreSQL username if it's different.</li>
</ol>
<pre><code class="lang-bash">psql -f airlines_db.sql -U postgres
</code></pre>
<p>This command will execute the SQL script and create the "airlines" database with the <code>bookings</code>, <code>flights</code>, and <code>airports_data</code> tables.</p>
<h3 id="heading-schema-description">Schema Description</h3>
<p>The main schema in the database is <code>bookings</code>. Let's take a closer look at the tables in the "airlines" database:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/Screenshot-2023-10-29-115228.png" alt="Image" width="600" height="400" loading="lazy">
<em>Schema Diagram</em></p>
<h4 id="heading-table-bookingsbookings">Table <code>bookings.bookings</code></h4>
<p>The "bookings" table is designed to store crucial information about bookings made for flights. Each booking is uniquely identified by the <code>book_ref</code>, which is a <code>character(6)</code> field. The <code>total_amount</code> field is a <code>numeric(10,2)</code> type and represents the total cost of the booking. </p>
<p>To track the booking date and time, the table includes a <code>book_date</code> field of type <code>bigint</code>. This table serves as the central repository for booking data and is essential for tracking passenger reservations, costs, and booking dates.</p>
<h4 id="heading-table-bookingsflights">Table <code>bookings.flights</code></h4>
<p>The "flights" table is dedicated to capturing comprehensive details about flights, including information about their statuses, scheduled and actual times of departure and arrival, and other important flight-related data. </p>
<p>The primary key for this table is the <code>flight_id</code>, an <code>integer</code> identifier. Each flight is associated with a specific flight number denoted by the <code>flight_no</code> field, a <code>character(6)</code> type. </p>
<p>To understand the flight's origin and destination, the <code>departure_airport</code> and <code>arrival_airport</code> fields store the departure and arrival airport codes as <code>character(3)</code> types, respectively. </p>
<p>The <code>status</code> field is a <code>character varying(20)</code> that records the flight's status, which must be one of 'On Time,' 'Delayed,' 'Departed,' 'Arrived,' 'Scheduled,' or 'Cancelled.' The table also includes fields for scheduled departure and arrival times (<code>scheduled_departure</code> and <code>scheduled_arrival</code>) and actual departure and arrival times (<code>actual_departure</code> and <code>actual_arrival</code>). </p>
<p>Furthermore, this table establishes two essential foreign keys: <code>flights_arrival_airport_fkey</code> and <code>flights_departure_airport_fkey</code>, which link to the <code>airport_code</code> in the "airports_data" table. This establishes connections between flights and their respective departure and arrival airports.</p>
<h4 id="heading-table-bookingsairportsdata">Table <code>bookings.airports_data</code></h4>
<p>The "airports_data" table serves as a repository for data related to airports and their geographic locations. Each airport is identified by a unique <code>character(3)</code> code stored in the <code>airport_code</code> field, which also serves as the primary key. </p>
<p>The <code>timezone</code> field, of type <code>text</code>, records the specific timezone of the airport, providing essential information for scheduling and operational purposes. The <code>airport_name</code> field is a <code>character varying</code> type that holds the name of the airport. Additionally, the table includes the <code>city</code> field as a <code>character varying</code> type, indicating the city in which the airport is situated. </p>
<p>These details enable the "airports_data" table to provide a comprehensive overview of airport locations and information. This serves as a reference for the "flights" table through the <code>flights_arrival_airport_fkey</code> and <code>flights_departure_airport_fkey</code> foreign keys, facilitating the association between flights and their respective departure and arrival airports.</p>
<h2 id="heading-how-to-set-up-logging-and-environment-variables">How to Set Up Logging and Environment Variables</h2>
<p>In this section, we'll configure logging to provide informative messages and handle errors throughout the code. We'll also set up environment variables to securely store sensitive information and configuration parameters. These practices enhance code readability, maintainability, and security.</p>
<h3 id="heading-logging-configuration">Logging Configuration</h3>
<p>We will utilize Python's built-in <code>logging</code> module to configure a logging system. Logging is essential for tracking the execution flow of the code and capturing important information or errors. </p>
<p>The <code>logging.basicConfig</code> method is called to define the format of log messages and set the logging level to <code>INFO</code>.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> logging

logging.basicConfig(
    format=<span class="hljs-string">"%(asctime)s | %(levelname)s : %(message)s"</span>, level=logging.INFO
)
</code></pre>
<ul>
<li><strong>Format</strong>: The <code>format</code> parameter specifies the format of log messages. In this case, each log entry includes a timestamp, log level (for example, INFO, ERROR), and the actual log message.</li>
<li><strong>Log Levels</strong>: We set the logging level to <code>INFO</code>, which means the logger will record informational messages. You can also use higher severity levels, such as <code>WARNING</code> or <code>ERROR</code>, for more critical issues.</li>
</ul>
<p>You can learn more about logging in Python in <a target="_blank" href="https://earthly.dev/blog/logging-in-python/">this tutorial</a>.</p>
<h3 id="heading-how-to-manage-environment-variables">How to Manage Environment Variables</h3>
<p>We will create a <code>.env</code> file to manage environment variables. Environment variables are used to store sensitive information and configuration settings, allowing us to keep such data separate from the code. </p>
<p>In this case, we set environment variables for email credentials and database connection details.</p>
<pre><code><span class="hljs-keyword">export</span> EMAIL=
<span class="hljs-keyword">export</span> PASSWORD=
<span class="hljs-keyword">export</span> EMAIL_PORT=<span class="hljs-number">587</span>
<span class="hljs-keyword">export</span> SMTP_SERVER=smtp.gmail.com
<span class="hljs-keyword">export</span> DB_HOSTNAME=localhost
<span class="hljs-keyword">export</span> DB_NAME=airlines
<span class="hljs-keyword">export</span> DB_PORT=<span class="hljs-number">5432</span>
<span class="hljs-keyword">export</span> DB_USERNAME=postgres
<span class="hljs-keyword">export</span> DB_PASSWORD=postgres
</code></pre><p>Here's a breakdown of the variables:</p>
<ul>
<li><strong>EMAIL</strong>: The email address to be used for sending emails.</li>
<li><strong>PASSWORD</strong>: The password associated with the email account.</li>
<li><strong>EMAIL_PORT</strong>: The port for the email server (for example, SMTP server). The default is 587 for secure email transmission (TLS/SSL).</li>
<li><strong>SMTP_SERVER</strong>: The SMTP server address, often specific to the email service provider.</li>
<li><strong>DB_HOSTNAME</strong>: The hostname or IP address of the PostgreSQL database server.</li>
<li><strong>DB_NAME</strong>: The name of the PostgreSQL database.</li>
<li><strong>DB_PORT</strong>: The port number for connecting to the database (default is 5432 for PostgreSQL).</li>
<li><strong>DB_USERNAME</strong>: The username for authenticating with the database.</li>
<li><strong>DB_PASSWORD</strong>: The password for the database user.</li>
</ul>
<p>Make sure you run <code>source .env</code> to load the environment variables.</p>
<p>By using environment variables, sensitive data like passwords and email credentials can be kept separate from the code, reducing the risk of accidental exposure or unauthorized access. The code can access these variables at runtime, ensuring security and flexibility in configuration.</p>
<h2 id="heading-how-to-extract-the-data-from-the-database">How to Extract the Data From the Database</h2>
<p>Let's start by setting the database configurations.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> logging
<span class="hljs-keyword">import</span> os

logging.basicConfig(
    format=<span class="hljs-string">"%(asctime)s | %(levelname)s : %(message)s"</span>, level=logging.INFO
)

DB_CONFIG = {
    <span class="hljs-string">"host"</span>: os.environ.get(<span class="hljs-string">"DB_HOSTNAME"</span>),
    <span class="hljs-string">"database"</span>: os.environ.get(<span class="hljs-string">"DB_NAME"</span>),
    <span class="hljs-string">"user"</span>: os.environ.get(<span class="hljs-string">"DB_USERNAME"</span>),
    <span class="hljs-string">"password"</span>: os.environ.get(<span class="hljs-string">"DB_PASSWORD"</span>),
}
</code></pre>
<p>The <code>DB_CONFIG</code> dictionary is used to store the configuration parameters for connecting to the PostgreSQL database. These parameters include the host, database name, username, and password. These values can be set through environment variables.</p>
<h3 id="heading-how-to-connect-to-the-database">How to Connect to the Database</h3>
<p>Before we extract the data from the database, we need to connect to our database. We will use the <code>psycopg2</code> library to connect to the PostgreSQL database.</p>
<p>We will start by defining a <code>DataExporter</code> class that will contain methods to extract the database and generate the Excel sheet.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataExporter</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Initialize the DataExporter with the database configuration."""</span>
        self.db_config = DB_CONFIG
</code></pre>
<p>The class constructor initializes the <code>DataExporter</code> with the database configuration stored in the <code>DB_CONFIG</code> <a target="_blank" href="https://blog.ashutoshkrris.in/everything-you-need-to-know-about-python-dictionaries">dictionary</a>.</p>
<p>Next, let's define a method that connects to the database.</p>
<pre><code class="lang-python">...
<span class="hljs-keyword">import</span> psycopg2

...

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataExporter</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Initialize the DataExporter with the database configuration."""</span>
        self.db_config = DB_CONFIG

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__connect_to_database</span>(<span class="hljs-params">self</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
        <span class="hljs-string">"""
        Establish a connection to the PostgreSQL database.

        Raises:
            Exception: If a connection to the database cannot be established.
        """</span>
        <span class="hljs-keyword">try</span>:
            self.conn = psycopg2.connect(**self.db_config)
            self.cursor = self.conn.cursor()
            logging.info(<span class="hljs-string">"Connected to the database"</span>)
        <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
            logging.error(
                <span class="hljs-string">"Failed to connect to the database with error: %s"</span>, e)
            <span class="hljs-keyword">raise</span>
</code></pre>
<p>The <code>__connect_to_database</code> private method is responsible for establishing a connection to the PostgreSQL database. It uses the <code>psycopg2</code> library to create a connection and a cursor for executing SQL queries. If the connection fails, it logs an error and raises an exception.</p>
<p>You can learn more about exception handling in Python <a target="_blank" href="https://blog.ashutoshkrris.in/exception-handling-in-python">here</a>.</p>
<h3 id="heading-how-to-fetch-data-from-the-database">How to Fetch Data from the Database</h3>
<p>Now we'll define another private method that connects to the database and fetches the total number of bookings and the total amount from the database.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataExporter</span>:</span>
    ...

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__fetch_from_database</span>(<span class="hljs-params">self, start_timestamp, end_timestamp</span>) -&gt; list | <span class="hljs-keyword">None</span>:</span>
        <span class="hljs-string">"""
        Fetch booking data from the database for a given time range.

        Args:
            start_timestamp (datetime): The start of the time range.
            end_timestamp (datetime): The end of the time range.

        Returns:
            list: A list containing booking data (num_bookings, total_amount) or None if an error occurs.
        """</span>
        self.__connect_to_database()
        query = <span class="hljs-string">f"""
        SELECT COUNT(*) AS num_bookings, SUM(total_amount) AS total_amount
        FROM bookings
        WHERE book_date &gt;= <span class="hljs-subst">{int(start_timestamp.timestamp()) * <span class="hljs-number">1000</span>}</span> AND book_date &lt;= <span class="hljs-subst">{int(end_timestamp.timestamp()) * <span class="hljs-number">1000</span>}</span>
        """</span>
        logging.info(
            <span class="hljs-string">"Exracting bookings data from database for start timestamp=%s and end_timestamp=%s"</span>,
            start_timestamp,
            end_timestamp,
        )
        result = <span class="hljs-literal">None</span>
        <span class="hljs-keyword">try</span>:
            self.cursor.execute(query)
            result = list(self.cursor.fetchone())
            result.append(
                <span class="hljs-string">f'<span class="hljs-subst">{start_timestamp.strftime(<span class="hljs-string">"%d %b, %Y"</span>)}</span> - <span class="hljs-subst">{end_timestamp.strftime(<span class="hljs-string">"%d %b, %Y"</span>)}</span>'</span>
            )
            logging.info(
                <span class="hljs-string">"Successfully exracted bookings data from database for start timestamp=%s and end_timestamp=%s"</span>,
                start_timestamp,
                end_timestamp,
            )
        <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
            logging.error(
                <span class="hljs-string">"Error occurred while extracting bookings data from database: %s"</span>, e
            )
        <span class="hljs-keyword">return</span> result
</code></pre>
<p>This private method retrieves booking data from the database for a specified time range. </p>
<p>It takes two <code>datetime</code> objects as arguments, <code>start_timestamp</code> and <code>end_timestamp</code>. It also constructs a SQL query to retrieve the count of bookings and the total booking amount for that time range. </p>
<p>The query is executed, and if it's successful, the method returns the data as a tuple. We convert the tuple into a list and append the timeframe for which data was extracted to the list. If an error occurs during the database interaction, it logs an error and returns <code>None</code>.</p>
<p>Using the above method, you can extract booking data for various timeframes, whether it's for a week, a month, a year, or any custom time range of your choice.</p>
<h2 id="heading-how-to-structure-the-booking-data-with-the-bookinginfo-class">How to Structure the Booking Data with the <code>BookingInfo</code> Class</h2>
<p>In this section, we will define a <code>BookingInfo</code> class in <code>booking_info.py</code>, which serves as a structured container for booking data retrieved from the database. The class encapsulates booking-related information, making it easier to work with and present the data. </p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> decimal <span class="hljs-keyword">import</span> Decimal


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BookingInfo</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, data_list: list</span>):</span>
        <span class="hljs-string">"""
        Initialize BookingInfo with data from the database.

        Args:
            data_list (list): A list containing booking data (total_bookings, total_amount, timestamp).

        Note:
            The total_amount is converted to a Decimal type.

        """</span>
        self.__total_bookings, self.__total_amount, self.__timestamp = data_list
        self.__total_amount = Decimal(self.__total_amount) <span class="hljs-keyword">if</span> self.__total_amount <span class="hljs-keyword">else</span> Decimal(<span class="hljs-number">0</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__str__</span>(<span class="hljs-params">self</span>) -&gt; str:</span>
        <span class="hljs-string">"""
        Return a string representation of BookingInfo.

        Returns:
            str: A string in the format "Total Bookings: X, Total Amount: $Y".

        """</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Total Bookings: <span class="hljs-subst">{self.__total_bookings}</span>, Total Amount: $<span class="hljs-subst">{self.__total_amount}</span>"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_total_bookings</span>(<span class="hljs-params">self</span>) -&gt; int:</span>
        <span class="hljs-string">"""
        Get the total number of bookings.

        Returns:
            int: The total number of bookings.

        """</span>
        <span class="hljs-keyword">return</span> self.__total_bookings

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_total_amount</span>(<span class="hljs-params">self</span>) -&gt; Decimal:</span>
        <span class="hljs-string">"""
        Get the total booking amount as a Decimal.

        Returns:
            Decimal: The total booking amount.

        """</span>
        <span class="hljs-keyword">return</span> self.__total_amount

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_timestamp</span>(<span class="hljs-params">self</span>) -&gt; str:</span>
        <span class="hljs-string">"""
        Get the timestamp associated with the booking data.

        Returns:
            str: The timestamp as a string.

        """</span>
        <span class="hljs-keyword">return</span> self.__timestamp
</code></pre>
<p>The <code>BookingInfo</code> class is designed to organize and represent booking data returned from the database. It receives a list of values containing total bookings, total booking amount, and a timestamp as input and converts the total amount to a Decimal type. The class offers methods for accessing and presenting this data in a structured manner.</p>
<p>The constructor of the <code>BookingInfo</code> class takes a <code>data_list</code> as input, which is expected to be a list containing the following elements:</p>
<ul>
<li><code>total_bookings</code>: An integer representing the total number of bookings.</li>
<li><code>total_amount</code>: A floating-point value representing the total booking amount.</li>
<li><code>timestamp</code>: A timestamp associated with the booking data.</li>
</ul>
<p>The <code>__init__</code> method initializes private instance variables (<code>__total_bookings</code>, <code>__total_amount</code>, and <code>__timestamp</code>) with the values from the <code>data_list</code>. It also converts the <code>__total_amount</code> to a decimal type for precise handling of monetary values.</p>
<p>The <code>__str__</code> method is implemented to provide a string representation of the <code>BookingInfo</code> object. It returns a string in the format "Total Bookings: X, Total Amount: $Y", where <code>X</code> is the total number of bookings and <code>Y</code> is the total booking amount formatted as dollars.</p>
<h3 id="heading-getter-methods">Getter Methods</h3>
<p>The class provides three getter methods to access the encapsulated data:</p>
<ul>
<li><code>get_total_bookings()</code>: Returns the total number of bookings as an integer.</li>
<li><code>get_total_amount()</code>: Returns the total booking amount as a Decimal type.</li>
<li><code>get_timestamp()</code>: Returns the timestamp associated with the booking data as a string.</li>
</ul>
<p>By encapsulating the booking data within the <code>BookingInfo</code> class, the code is more organized, readable, and reusable. This structured approach simplifies the handling of booking information throughout the application, making it more intuitive to work with and present the data.</p>
<h2 id="heading-how-to-convert-the-data-into-an-excel-sheet">How to Convert the Data into an Excel Sheet</h2>
<p>Now that you can retrieve data from the database for a specific time range, you can also generate an Excel sheet based on the extracted data. </p>
<p>To do this, let's define another private method to create the Excel sheet.</p>
<pre><code class="lang-python">...
<span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd

<span class="hljs-keyword">from</span> booking_info <span class="hljs-keyword">import</span> BookingInfo


...

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataExporter</span>:</span>

    ...

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__convert_to_excelsheet</span>(<span class="hljs-params">self, data: list, sheet_name: str</span>):</span>
        <span class="hljs-string">"""
        Convert the fetched data into an Excel sheet.

        Args:
            data (list): A list containing booking data.
            sheet_name (str): Name of the Excel sheet to be created.

        Raises:
            ValueError: If there is an error in converting data to an Excel sheet.
        """</span>
        <span class="hljs-keyword">try</span>:
            booking_info = BookingInfo(data)
            data = {
                <span class="hljs-string">""</span>: [<span class="hljs-string">"Total Bookings"</span>, <span class="hljs-string">"Total Amount ($)"</span>],
                booking_info.get_timestamp(): [
                    booking_info.get_total_bookings(),
                    booking_info.get_total_amount(),
                ],
            }
            logging.info(<span class="hljs-string">"Converting the data into pandas dataframe"</span>)
            df = pd.DataFrame(data)
            logging.info(<span class="hljs-string">"Inserting the data into the excelsheet"</span>)
            <span class="hljs-keyword">with</span> pd.ExcelWriter(sheet_name, engine=<span class="hljs-string">"xlsxwriter"</span>) <span class="hljs-keyword">as</span> writer:
                df.to_excel(writer, sheet_name=<span class="hljs-string">"Sheet1"</span>, index=<span class="hljs-literal">False</span>)
            logging.info(<span class="hljs-string">"Successfully inserted data into the excelsheet"</span>)
        <span class="hljs-keyword">except</span> ValueError <span class="hljs-keyword">as</span> e:
            logging.error(<span class="hljs-string">"Error converting data into excel: %s"</span>, e)
</code></pre>
<p>The <code>__convert_to_excelsheet</code> method within the <code>DataExporter</code> class is responsible for structuring and converting extracted booking data into an Excel sheet. </p>
<p>It accepts two input parameters. The first parameter, <code>data</code>, is expected to be a list containing specific booking data. This data includes the total number of bookings, the total booking amount, and a timestamp for which data was extracted. The second parameter, <code>sheet_name</code>, represents the desired name for the Excel sheet that will contain the formatted data.</p>
<p>A key aspect of the method is the structuring of the data. To achieve this, the method initiates the creation of a <code>BookingInfo</code> object, referred to as <code>booking_info</code>. The <code>BookingInfo</code> object provides a structured representation of the booking data, which simplifies the subsequent formatting and presentation.</p>
<p>Following the creation of the <code>booking_info</code> object, a new dictionary called <code>data</code> is generated. This dictionary is designed to structure the data in a format suitable for conversion into an Excel sheet. </p>
<p>The dictionary consists of two key-value pairs:</p>
<ul>
<li>The first pair uses an empty string as the key and contains a list with two header values, "Total Bookings" and "Total Amount ($)".</li>
<li>The second pair uses the timestamp obtained from <code>booking_info.get_timestamp()</code> as the key and includes a list with two elements: the total number of bookings (<code>booking_info.get_total_bookings()</code>) and the total booking amount (<code>booking_info.get_total_amount()</code>).</li>
</ul>
<p>This dictionary allows the data to be inserted in the excel sheet as below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/Screenshot-2023-10-29-135512.png" alt="Image" width="600" height="400" loading="lazy">
<em>Sample Excel Sheet</em></p>
<p>Then, the structured <code>data</code> dictionary is converted into a pandas DataFrame, referred to as <code>df</code>. Dataframes are a commonly used data structures for handling tabular data in Python. This step streamlines the manipulation and export of the data for further processing or visualization. </p>
<p>To create the Excel sheet, the code uses the <code>pd.ExcelWriter</code> context manager with the "xlsxwriter" engine. This context manager ensures that the Excel file is appropriately prepared for data insertion. The <code>sheet_name</code> parameter is supplied to specify the name of the sheet within the Excel file.</p>
<p>The data within the DataFrame, <code>df</code>, is then written to the Excel sheet. The <code>to_excel</code> method is used in conjunction with the <code>writer</code> object, and the <code>index</code> parameter is set to <code>False</code>. This specific configuration excludes the default row numbers that are typically included in Excel sheets.</p>
<h2 id="heading-how-to-combine-the-functionalities">How to Combine the Functionalities</h2>
<p>Now let's write a public method that the users can use to extract the data from the database and convert the extracted data into the Excel sheet file.</p>
<pre><code class="lang-python">...


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataExporter</span>:</span>

    ...

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate_excelsheet</span>(<span class="hljs-params">
        self,
        start_timestamp: datetime,
        end_timestamp: datetime,
        sheet_name: str = <span class="hljs-string">"Bookings Data.xlsx"</span>,
    </span>) -&gt; bool:</span>
        <span class="hljs-string">"""
        Generate an Excel sheet with booking data for a specified time range.

        Args:
            start_timestamp (datetime): The start of the time range.
            end_timestamp (datetime): The end of the time range.
            sheet_name (str, optional): Name of the Excel sheet to be created. Defaults to "Bookings Data.xlsx".

        Returns:
            bool: True if excelsheet was generated successfully else False

        Note:
            This method logs errors but does not raise exceptions to avoid breaking the workflow.
        """</span>
        data = self.__fetch_from_database(start_timestamp, end_timestamp)
        <span class="hljs-keyword">if</span> data <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span>:
            self.__convert_to_excelsheet(data, sheet_name)
            <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>
        <span class="hljs-keyword">else</span>:
            logging.error(<span class="hljs-string">"No data to convert generate excelsheet"</span>)
            <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
</code></pre>
<p>This method accepts several parameters, including <code>start_timestamp</code> and <code>end_timestamp</code>, which define the beginning and end of the time period for data extraction. There's also an optional <code>sheet_name</code> parameter that allows the user to specify the name of the Excel sheet. By default, the sheet is named "Bookings Data.xlsx" to provide a convenient default option.</p>
<p>Upon execution, the method initiates the data retrieval process by calling the <code>__fetch_from_database</code> method, an internal private method of the class, with the specified time range. </p>
<p>If the data retrieval is successful and data is available, the method proceeds to call the <code>__convert_to_excelsheet</code> method. This structures and formats the data for insertion into the Excel sheet. </p>
<p>If, on the other hand, no data is available for the provided time range, the method logs an error message and returns "False" to indicate that the Excel sheet generation was unsuccessful.</p>
<h2 id="heading-how-to-send-an-email-with-the-bookings-data-report">How to Send an Email with the Bookings Data Report</h2>
<p>In this section, you will learn how you can <a target="_blank" href="https://blog.ashutoshkrris.in/how-to-send-emails-using-python">use Python to send an email</a> with a bookings data report as an attachment.</p>
<p>Create a <code>mailer.py</code> file and add the following content:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> logging
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> smtplib
<span class="hljs-keyword">import</span> ssl

<span class="hljs-keyword">from</span> email <span class="hljs-keyword">import</span> encoders
<span class="hljs-keyword">from</span> email.mime.base <span class="hljs-keyword">import</span> MIMEBase
<span class="hljs-keyword">from</span> email.mime.multipart <span class="hljs-keyword">import</span> MIMEMultipart
<span class="hljs-keyword">from</span> email.mime.text <span class="hljs-keyword">import</span> MIMEText

logging.basicConfig(
    format=<span class="hljs-string">"%(asctime)s | %(levelname)s : %(message)s"</span>, level=logging.INFO
)

SMTP_SERVER = os.environ.get(<span class="hljs-string">"SMTP_SERVER"</span>)
PORT = os.environ.get(<span class="hljs-string">"EMAIL_PORT"</span>)
EMAIL = os.environ.get(<span class="hljs-string">"EMAIL"</span>)
PASSWORD = os.environ.get(<span class="hljs-string">"PASSWORD"</span>)


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_email</span>(<span class="hljs-params">to_email: str, subject: str, attachment_name: str</span>):</span>
    <span class="hljs-string">"""
    Send an email with an attachment to the specified recipient.

    Args:
        to_email (str): The recipient's email address.
        subject (str): The subject of the email.
        attachment_name (str): The filename of the attachment.

    Note:
        This function assumes that the SMTP server requires TLS encryption.

    Raises:
        smtplib.SMTPException: If there is an issue with sending the email.

    """</span>
    message = MIMEMultipart()
    message[<span class="hljs-string">"From"</span>] = EMAIL
    message[<span class="hljs-string">"To"</span>] = to_email
    message[<span class="hljs-string">"Subject"</span>] = subject
    body = <span class="hljs-string">"Hi there\n\nPlease find attached your report.\n\nThanks"</span>

    message.attach(MIMEText(body, <span class="hljs-string">"plain"</span>))

    <span class="hljs-keyword">with</span> open(attachment_name, <span class="hljs-string">"rb"</span>) <span class="hljs-keyword">as</span> file:
        part = MIMEBase(
            <span class="hljs-string">"application"</span>, <span class="hljs-string">"vnd.openxmlformats-officedocument.spreadsheetml.sheet"</span>
        )
        part.set_payload(file.read())

    encoders.encode_base64(part)

    part.add_header(
        <span class="hljs-string">"Content-Disposition"</span>,
        <span class="hljs-string">f"attachment; filename= <span class="hljs-subst">{attachment_name}</span>"</span>,
    )

    logging.info(<span class="hljs-string">f"Attaching <span class="hljs-subst">{attachment_name}</span> to the email"</span>)
    message.attach(part)
    text = message.as_string()

    context = ssl.create_default_context()
    <span class="hljs-keyword">with</span> smtplib.SMTP(SMTP_SERVER, PORT) <span class="hljs-keyword">as</span> server:
        logging.info(<span class="hljs-string">f"Sending email to <span class="hljs-subst">{to_email}</span>"</span>)
        server.starttls(context=context)
        server.login(EMAIL, PASSWORD)
        server.sendmail(EMAIL, to_email, text)
        logging.info(<span class="hljs-string">f"Successfully sent the email to <span class="hljs-subst">{to_email}</span>"</span>)
</code></pre>
<p>As usual, we have configured the logger and environment variables in our script. </p>
<p>The core functionality is encapsulated within the <code>send_email</code> function. This function takes three parameters:</p>
<ol>
<li><code>to_email</code>: The recipient's email address.</li>
<li><code>subject</code>: The subject of the email.</li>
<li><code>attachment_name</code>: The filename of the attachment, which should be the bookings data report in this context.</li>
</ol>
<p>Within the function, we construct an email message using the <code>MIMEMultipart</code> class. This message includes the sender's email address, recipient's email address, subject, and a plain text body with a simple message.</p>
<p>The script allows attaching the bookings data report as an attachment. It reads the attachment file, encodes it, and adds it to the email message. This ensures that the recipient can easily access and download the data report from the email.</p>
<p>You can learn how you can add attachments while sending emails using Python <a target="_blank" href="https://blog.ashutoshkrris.in/how-to-send-emails-using-python#heading-including-attachments">here</a>.</p>
<p>The <code>create_default_context</code> function from the <code>ssl</code> library creates a secure SSL context for email communication. Finally, the script connects to the SMTP server, logs in using the sender's email address and password, sends the email, and logs a success message upon successful transmission.</p>
<h2 id="heading-how-to-test-the-flow">How to Test the Flow</h2>
<p>Let's finally test the flow of the application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/finally-about-time.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In this section, we will automate the monthly reports. Create a <code>main.py</code> file and add the following content:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> exporter <span class="hljs-keyword">import</span> DataExporter
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime
<span class="hljs-keyword">from</span> mailer <span class="hljs-keyword">import</span> send_email

start_timestamp = datetime(<span class="hljs-number">2023</span>, <span class="hljs-number">5</span>, <span class="hljs-number">28</span>, <span class="hljs-number">00</span>, <span class="hljs-number">00</span>, <span class="hljs-number">00</span>)  <span class="hljs-comment"># May 28 2023 00:00:00</span>
end_timestamp = datetime(<span class="hljs-number">2023</span>, <span class="hljs-number">8</span>, <span class="hljs-number">20</span>, <span class="hljs-number">23</span>, <span class="hljs-number">59</span>, <span class="hljs-number">59</span>)  <span class="hljs-comment"># Aug 20 2023 23:59:59</span>

exporter = DataExporter()
<span class="hljs-keyword">if</span> exporter.generate_excelsheet(
        start_timestamp, end_timestamp, sheet_name=<span class="hljs-string">"Bookings Data.xlsx"</span>):
    send_email(<span class="hljs-string">"myemail@gmail.com"</span>, <span class="hljs-string">"Your Report"</span>, <span class="hljs-string">"Bookings Data.xlsx"</span>)
</code></pre>
<p>In the above code, we create two timestamp objects, <code>start_timestamp</code> and <code>end_timestamp</code>, to specify a time range. We have the start date set to May 28, 2023 at midnight and the end date set to August 20, 2023 just before midnight. </p>
<p>Next, we create an instance of the <code>DataExporter</code> class, which handles the data export and Excel sheet generation. The <code>generate_excelsheet</code> method of this instance is called with the previously defined timestamps to create a report related to bookings. </p>
<p>Finally, the code sends an email with the generated Excel sheet as an attachment using the <code>send_email</code> function.</p>
<h2 id="heading-how-to-schedule-the-application">How to Schedule the Application</h2>
<p>Next, our goal is to automate the report scheduling process. We aim to schedule report deliveries for two distinct scenarios: on every Monday for the previous week's data, and on the 1st day of every month for the previous month's information. </p>
<p>To schedule the execution, you will need to install the <code>schedule</code> library:</p>
<pre><code class="lang-bash">pip install schedule
</code></pre>
<p>Once the library is installed, here's how you can do automate the monthly and weekly reports:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> schedule
<span class="hljs-keyword">from</span> exporter <span class="hljs-keyword">import</span> DataExporter
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime, timedelta
<span class="hljs-keyword">from</span> mailer <span class="hljs-keyword">import</span> send_email


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    today = datetime.now()
    sheet_name = <span class="hljs-string">"Bookings Data.xlsx"</span>

    <span class="hljs-keyword">if</span> today.weekday() == <span class="hljs-number">0</span>:  <span class="hljs-comment"># Check if it's Monday (0 means Monday)</span>
        <span class="hljs-comment"># It's Monday, fetch data for the previous week (Monday to Sunday)</span>
        start_timestamp = (today - timedelta(days=<span class="hljs-number">7</span>)
                           ).replace(hour=<span class="hljs-number">0</span>, minute=<span class="hljs-number">0</span>, second=<span class="hljs-number">0</span>, microsecond=<span class="hljs-number">0</span>)
        end_timestamp = (today - timedelta(days=<span class="hljs-number">1</span>)
                         ).replace(hour=<span class="hljs-number">23</span>, minute=<span class="hljs-number">59</span>, second=<span class="hljs-number">59</span>, microsecond=<span class="hljs-number">0</span>)
        sheet_name = <span class="hljs-string">"Weekly Report.xlsx"</span>
    <span class="hljs-keyword">elif</span> today.day == <span class="hljs-number">29</span>:
        <span class="hljs-comment"># It's the 1st day of the month, fetch data for the last month</span>
        start_timestamp = (today.replace(day=<span class="hljs-number">1</span>) - timedelta(days=<span class="hljs-number">1</span>)
                           ).replace(day=<span class="hljs-number">1</span>, hour=<span class="hljs-number">0</span>, minute=<span class="hljs-number">0</span>, second=<span class="hljs-number">0</span>, microsecond=<span class="hljs-number">0</span>)
        end_timestamp = (today.replace(day=<span class="hljs-number">1</span>) - timedelta(days=<span class="hljs-number">1</span>)
                         ).replace(hour=<span class="hljs-number">23</span>, minute=<span class="hljs-number">59</span>, second=<span class="hljs-number">59</span>, microsecond=<span class="hljs-number">0</span>)
        sheet_name = <span class="hljs-string">"Monthly Report.xlsx"</span>

    exporter = DataExporter()
    exporter.generate_excelsheet(
        start_timestamp, end_timestamp, sheet_name)

    send_email(<span class="hljs-string">"youremail@gmail.com"</span>,
               <span class="hljs-string">"Your Report"</span>, sheet_name)


schedule.every().day.at(<span class="hljs-string">"00:00"</span>).do(main)

<span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
    schedule.run_pending()
</code></pre>
<p>The above script uses the <code>schedule</code> library to run the <code>main</code> function daily at midnight. The <code>main</code> function calculates the timestamps for data extraction and Excel sheet generation. After generating the Excel sheet, the script sends it via email to a specified recipient.</p>
<p>If the script runs on a Monday, it sets up to generate a weekly report. It calculates the <code>start_timestamp</code> and <code>end_timestamp</code> for the previous week. The <code>start_timestamp</code> is set to the previous Monday at midnight (00:00:00), and the <code>end_timestamp</code> is set to the previous Sunday just before midnight (23:59:59). The Excel sheet is named "Weekly Report.xlsx."</p>
<p>On the 1st day of the month, the script shifts its focus to generating a monthly report. It calculates the <code>start_timestamp</code> and <code>end_timestamp</code> to encompass the entire previous month. The <code>start_timestamp</code> is set to the first day of the previous month at midnight (00:00:00), while the <code>end_timestamp</code> is set to the last day of the previous month just before midnight (23:59:59). The Excel sheet is named "Monthly Report.xlsx."</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>In this tutorial, you learned how you can leverage Python to automate generating a report and sending it to email recipients. I hope you found the tutorial helpful!</p>
<h3 id="heading-future-scope">Future Scope</h3>
<ul>
<li>You can add the email recipients in a database and fetch their list from there instead of hardcoding them in the code itself. This will make the application more configurable.</li>
<li>You can also use Cron Jobs to automate the execution of the script every day at midnight. In that case, you won't need the <code>schedule</code> library.</li>
</ul>
<p>Here's a link to the <a target="_blank" href="https://github.com/ashutoshkrris/report-automation">Github Code Repository</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Digital Products Store with Medusa and Next.js ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, you will learn how to build an e-book online store using Medusa and Next.js. Throughout the course of the article, we will: Utilize the Medusa Next.js Starter Template along with the Digital Products Recipe build the store. Enhance... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-digital-products-store-with-medusa-and-next-js/</link>
                <guid isPermaLink="false">66ba0e6b79b7f411df58dea1</guid>
                
                    <category>
                        <![CDATA[ ecommerce ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Tue, 17 Oct 2023 22:59:34 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/10/digital-products.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, you will learn how to build an e-book online store using Medusa and Next.js.</p>
<p>Throughout the course of the article, we will:</p>
<ol>
<li>Utilize the Medusa <a target="_blank" href="https://medusajs.com/nextjs-commerce/">Next.js Starter Template</a> along with the <a target="_blank" href="https://docs.medusajs.com/recipes/digital-products">Digital Products Recipe</a> build the store.</li>
<li>Enhance the product pages to suit digital products. This involves adding a button for previewing media content and displaying essential product details.</li>
<li>Refine the checkout process to make it more efficient for delivering digital products.</li>
<li>Create Next.js API routes to validate and conceal file paths for product downloads.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/rmjjaunyells0it99c7a.gif" alt="Image" width="600" height="400" loading="lazy">
<em>Demo of the final application</em></p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-what-is-medusa">What is Medusa?</a></li>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-starting-out">Starting Out</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-typescript-type-definitions">How to Set Up TypeScript Type Definitions</a></li>
<li><a class="post-section-overview" href="#heading-how-to-incorporate-e-book-previews-into-the-product-details">How to Incorporate e-Book Previews into the Product Details</a></li>
<li><a class="post-section-overview" href="#heading-how-to-offer-e-book-previews">How to Offer e-Book Previews</a></li>
<li><a class="post-section-overview" href="#heading-how-to-adjust-the-product-and-shipping-details">How to Adjust the Product and Shipping Details</a></li>
<li><a class="post-section-overview" href="#heading-how-to-simplify-the-checkout">How to Simplify the Checkout</a></li>
<li><a class="post-section-overview" href="#heading-how-to-deliver-digital-products">How to Deliver Digital Products</a></li>
</ol>
<p>Let's get started.</p>
<h2 id="heading-what-is-medusa">What is Medusa?</h2>
<p>Medusa is a suite of tools and modules specifically designed for e-Commerce products. </p>
<p>Using Medusa, you can build modularized commerce logic like <a target="_blank" href="https://docs.medusajs.com/modules/carts-and-checkout/overview">carts</a>, <a target="_blank" href="https://docs.medusajs.com/modules/products/overview">products</a>, and <a target="_blank" href="https://docs.medusajs.com/modules/orders/overview">order management</a>. It also provide tools that help you orchestrate powerful ecommerce websites, POS applications, commerce-enabled products, and everything in between.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you get started with the tutorial, you should have installed:</p>
<ul>
<li><a target="_blank" href="https://docs.medusajs.com/tutorial/set-up-your-development-environment#nodejs">Node.js(V14 or later)</a></li>
<li><a target="_blank" href="https://docs.medusajs.com/tutorial/set-up-your-development-environment/#git">Git</a></li>
<li><a target="_blank" href="https://docs.medusajs.com/tutorial/set-up-your-development-environment#medusa-cli">Medusa CLI</a></li>
</ul>
<h2 id="heading-starting-out">Starting Out</h2>
<p>Using the Next.js starter, you can create a new Medusa app by running the following command:</p>
<pre><code class="lang-bash">npx create-medusa-app@latest --with-nextjs-starter
</code></pre>
<p>After that, you can opt to create a user account for admin panel access. Then, set up the backend infrastructure following the Medusa Digital Products Recipe. </p>
<p>Once the backend is set, create sample products through your Medusa admin interface. Make sure that these products include digital media files for previews and primary content. Also make sure to incorporate relevant product metadata values using key/value pairs linked to each product.</p>
<h2 id="heading-how-to-set-up-typescript-type-definitions">How to Set Up TypeScript Type Definitions</h2>
<p>If you’re using regular JavaScript, you can skip this step.</p>
<p>Before we continue, let's make sure to add in the necessary TypeScript type definitions for digital products in the Next.js storefront.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Product } <span class="hljs-keyword">from</span> <span class="hljs-string">"@medusajs/medusa"</span>
<span class="hljs-keyword">import</span> { ProductVariant } <span class="hljs-keyword">from</span> <span class="hljs-string">"@medusajs/product"</span>

<span class="hljs-keyword">export</span> enum ProductMediaVariantType {
  PREVIEW = <span class="hljs-string">"preview"</span>,
  MAIN = <span class="hljs-string">"main"</span>,
}

<span class="hljs-keyword">export</span> type ProductMedia = {
  <span class="hljs-attr">id</span>: string
  name?: string
  file?: string
  mime_type?: string
  created_at?: <span class="hljs-built_in">Date</span>
  updated_at?: <span class="hljs-built_in">Date</span>
  attachment_type?: ProductMediaVariantType
  variant_id?: string
  variants?: ProductMediaVariant[]
}

<span class="hljs-keyword">export</span> type ProductMediaVariant = {
  <span class="hljs-attr">id</span>: string
  <span class="hljs-attr">variant_id</span>: string
  <span class="hljs-attr">product_media_id</span>: string
  <span class="hljs-attr">type</span>: string
  <span class="hljs-attr">created_at</span>: <span class="hljs-built_in">Date</span>
  <span class="hljs-attr">updated_at</span>: <span class="hljs-built_in">Date</span>
}

<span class="hljs-keyword">export</span> type DigitalProduct = Omit&lt;Product, <span class="hljs-string">"variants"</span>&gt; &amp; {
  product_medias?: ProductMedia[]
  variants?: DigitalProductVariant[]
}

<span class="hljs-keyword">export</span> type DigitalProductVariant = ProductVariant &amp; {
  product_medias?: ProductMedia
}

      <span class="hljs-keyword">throw</span> err
    })

  <span class="hljs-keyword">return</span> product_medias[<span class="hljs-number">0</span>]
}
</code></pre>
<p>This code defines TypeScript types and interfaces for managing digital products and their associated media files in an e-commerce system. It introduces several crucial structures:</p>
<ol>
<li><code>ProductMedia</code>: This interface describes media files related to a product. These files can include images, documents, or any digital assets. It encompasses properties such as an <code>id</code> (a unique identifier for the media), <code>name</code> (an optional name for the media), <code>file</code> (representing the file path or URL), <code>mime_type</code> (the type of media, e.g., image/jpeg), <code>created_at</code> and <code>updated_at</code> timestamps, and <code>attachment_type</code> that categorizes the media as "preview" or "main." Additionally, a media item can have multiple variants, making it adaptable for various use cases.</li>
<li><code>ProductMediaVariant</code>: This interface represents different variants or versions of a product's media. Each variant has its unique <code>id</code>, <code>variant_id</code> (relating it to a specific product variant), <code>product_media_id</code> (linking it to a particular media item), and timestamps for <code>created_at</code> and <code>updated_at</code>.</li>
<li><code>DigitalProduct</code>: It extends the standard <code>Product</code> type by introducing an array called <code>product_medias</code>. This array enables the association of media files with a digital product, allowing the presentation of images or other media related to the product. The <code>variants</code> property is tailored for digital products, adapting the generic <code>ProductVariant</code> to digital product-specific requirements.</li>
<li><code>DigitalProductVariant</code>: This type, an extension of <code>ProductVariant</code>, allows the linking of media files with a specific variant of a digital product. This is particularly valuable for showcasing different digital assets associated with each variant of the product.</li>
</ol>
<h2 id="heading-how-to-incorporate-e-book-previews-into-the-product-details">How to Incorporate e-Book Previews into the Product Details</h2>
<p>Now, let's move forward by adding e-book previews to our product detail page. To do this, we'll get the media previews linked to the currently selected product variant. </p>
<p>In the <code>src/lib/data/index.ts</code> file, we'll create a function to get these previews based on the chosen variant.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ... other imports</span>
<span class="hljs-keyword">import</span> { DigitalProduct, ProductMedia } <span class="hljs-keyword">from</span> <span class="hljs-string">"types/product-media"</span>

<span class="hljs-comment">// ... rest of the functions</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getProductMediaPreviewByVariant</span>(<span class="hljs-params">
  variant: Variant
</span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">ProductMedia</span>&gt; </span>{
  <span class="hljs-keyword">const</span> { product_medias } = <span class="hljs-keyword">await</span> medusaRequest(<span class="hljs-string">"GET"</span>, <span class="hljs-string">`/product-media`</span>, {
    <span class="hljs-attr">query</span>: {
      <span class="hljs-attr">variant_ids</span>: variant.id,
      <span class="hljs-attr">expand</span>: [<span class="hljs-string">"variants"</span>],
    },
  })
    .then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> res.body)
    .catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
      <span class="hljs-keyword">throw</span> err
    })

  <span class="hljs-keyword">return</span> product_medias[<span class="hljs-number">0</span>]
}
</code></pre>
<p>This function is responsible for fetching information related to a specific product variant. It does this by making an HTTP request to the <code>/product-media</code> endpoint. It takes one argument, <code>variant</code>, which is expected to be of type <code>Variant</code>. The request includes query parameters specifying the <code>variant_ids</code> and requests additional details about related "variants". </p>
<p>The function awaits the response from the HTTP request and extracts the response body, which is assumed to be an array of product media objects. It then returns the first product media object from this array, presuming there is at least one such object. If an error occurs during the request, it catches the error and rethrows it.</p>
<h2 id="heading-how-to-offer-e-book-previews">How to Offer e-Book Previews</h2>
<p>To give customers a glimpse of the e-book's content, we'll provide a preview PDF with the first few pages. </p>
<p>To do this, we'll set up a Next API route to manage file downloads while keeping the file's location private. We'll also create a component for a straightforward "download free preview" button. If a product variant has preview media, it will be shown in the product-actions component.</p>
<p>You can use the newly created <code>DigitalProduct</code> and <code>DigitalProductVariant</code> types to fix any TypeScript errors that you may encounter.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { NextRequest, NextResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/server"</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">GET</span>(<span class="hljs-params">req: NextRequest</span>) </span>{
  <span class="hljs-comment">// Get the file info from the URL</span>
  <span class="hljs-keyword">const</span> { filepath, filename } = <span class="hljs-built_in">Object</span>.fromEntries(req.nextUrl.searchParams)

  <span class="hljs-comment">// Fetch the PDF file</span>
  <span class="hljs-keyword">const</span> pdfResponse = <span class="hljs-keyword">await</span> fetch(filepath)

  <span class="hljs-comment">// Handle the case where the PDF could not be fetched</span>
  <span class="hljs-keyword">if</span> (!pdfResponse.ok) <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> NextResponse(<span class="hljs-string">"PDF not found"</span>, { <span class="hljs-attr">status</span>: <span class="hljs-number">404</span> })

  <span class="hljs-comment">// Get the PDF content as a buffer</span>
  <span class="hljs-keyword">const</span> pdfBuffer = <span class="hljs-keyword">await</span> pdfResponse.arrayBuffer()

  <span class="hljs-comment">// Define response headers</span>
  <span class="hljs-keyword">const</span> headers = {
    <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/pdf"</span>,
    <span class="hljs-string">"Content-Disposition"</span>: <span class="hljs-string">`attachment; filename="<span class="hljs-subst">${filename}</span>"`</span>, <span class="hljs-comment">// This sets the file name for the download</span>
  }

  <span class="hljs-comment">// Create a NextResponse with the PDF content and headers</span>
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">new</span> NextResponse(pdfBuffer, {
    <span class="hljs-attr">status</span>: <span class="hljs-number">200</span>,
    headers,
  })

  <span class="hljs-keyword">return</span> response
}
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"@modules/common/components/button"</span>
<span class="hljs-keyword">import</span> { ProductMedia } <span class="hljs-keyword">from</span> <span class="hljs-string">"types/product-media"</span>

type Props = {
  <span class="hljs-attr">media</span>: ProductMedia
}

<span class="hljs-keyword">const</span> ProductMediaPreview: React.FC&lt;Props&gt; = <span class="hljs-function">(<span class="hljs-params">{ media }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> downloadPreview = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">window</span>.location.href = <span class="hljs-string">`<span class="hljs-subst">${process.env.NEXT_PUBLIC_BASE_URL}</span>/api/download/preview?filepath=<span class="hljs-subst">${media.file}</span>&amp;filename=<span class="hljs-subst">${media.name}</span>`</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">Button</span> <span class="hljs-attr">variant</span>=<span class="hljs-string">"secondary"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{downloadPreview}</span>&gt;</span>
        Download free preview
      <span class="hljs-tag">&lt;/<span class="hljs-name">Button</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> ProductMediaPreview
</code></pre>
<p>The <code>GET</code> function is designed to handle incoming HTTP GET requests using the Next.js framework. It first extracts information from the request URL, specifically the <code>filepath</code> and <code>filename</code>, which are expected to be query parameters. It then attempts to fetch a PDF file from the specified <code>filepath</code>. If the PDF is successfully retrieved, it proceeds to convert the PDF content into a buffer.</p>
<p>In case the PDF retrieval fails, for instance, if the file is not found, it returns a response with a "PDF not found" message and a 404 status code, indicating a not found error.</p>
<p>If the PDF is successfully fetched, it defines response headers, specifying that the content type is "application/pdf" and setting the "Content-Disposition" header to control the behavior of file downloads. The <code>Content-Disposition</code> header is set to "attachment," and the <code>filename</code> parameter is used to suggest a filename for the downloaded PDF.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"@modules/common/components/button"</span>
<span class="hljs-keyword">import</span> { ProductMedia } <span class="hljs-keyword">from</span> <span class="hljs-string">"types/product-media"</span>

type Props = {
  <span class="hljs-attr">media</span>: ProductMedia
}

<span class="hljs-keyword">const</span> ProductMediaPreview: React.FC&lt;Props&gt; = <span class="hljs-function">(<span class="hljs-params">{ media }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> downloadPreview = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">window</span>.location.href = <span class="hljs-string">`<span class="hljs-subst">${process.env.NEXT_PUBLIC_BASE_URL}</span>/api/download/preview?filepath=<span class="hljs-subst">${media.file}</span>&amp;filename=<span class="hljs-subst">${media.name}</span>`</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">Button</span> <span class="hljs-attr">variant</span>=<span class="hljs-string">"secondary"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{downloadPreview}</span>&gt;</span>
        Download free preview
      <span class="hljs-tag">&lt;/<span class="hljs-name">Button</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> ProductMediaPreview
</code></pre>
<p> The above component displays a preview of a product's media along with a button to download a free preview of that media. The component receives a prop named <code>media</code>, which is expected to be of type <code>ProductMedia</code>.</p>
<p>Inside the component, there's a <code>downloadPreview</code> function that's called when a user clicks the "Download free preview" button. This function constructs a URL for downloading the preview using the <code>window.location.href</code> property. It combines the base URL from the environment variable <code>NEXT_PUBLIC_BASE_URL</code> with the "/api/download/preview" route and includes query parameters for the file path and file name, which are extracted from the <code>media</code> prop.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ...other imports</span>
<span class="hljs-keyword">import</span> ProductMediaPreview <span class="hljs-keyword">from</span> <span class="hljs-string">"../product-media-preview"</span>
<span class="hljs-keyword">import</span> { getProductMediaPreviewByVariant } <span class="hljs-keyword">from</span> <span class="hljs-string">"@lib/data"</span>

<span class="hljs-keyword">const</span> ProductActions: React.FC&lt;ProductActionsProps&gt; = <span class="hljs-function">(<span class="hljs-params">{ product }</span>) =&gt;</span> {
    <span class="hljs-comment">// ...other code</span>

  <span class="hljs-keyword">const</span> [productMedia, setProductMedia] = useState({} <span class="hljs-keyword">as</span> ProductMedia)

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> getProductMedia = <span class="hljs-keyword">async</span> () =&gt; {
      <span class="hljs-keyword">if</span> (!variant) <span class="hljs-keyword">return</span>
      <span class="hljs-keyword">await</span> getProductMediaPreviewByVariant(variant).then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
        setProductMedia(res)
      })
    }
    getProductMedia()
  }, [variant])

  <span class="hljs-keyword">return</span> (
            <span class="hljs-comment">// ...other code</span>

      {productMedia &amp;&amp; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ProductMediaPreview</span> <span class="hljs-attr">media</span>=<span class="hljs-string">{productMedia}</span> /&gt;</span></span>}

      &lt;Button onClick={addToCart}&gt;
        {!inStock ? <span class="hljs-string">"Out of stock"</span> : <span class="hljs-string">"Add to cart"</span>}
      &lt;/Button&gt;
    &lt;/div&gt;
  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ProductActions
</code></pre>
<p>This component is responsible for displaying product-related actions, such as adding a product to the cart, and showing product media preview if available. It leverages asynchronous operations to fetch the media data based on the provided <code>variant</code>, making it a dynamic and interactive component.</p>
<h2 id="heading-how-to-adjust-the-product-and-shipping-details">How to Adjust the Product and Shipping Details</h2>
<p>Because product and shipping information differs between digital and physical products, we'll make changes to these sections on the product page as needed.</p>
<h3 id="heading-how-to-add-product-details">How to Add Product Details</h3>
<p>I've added product details to the e-book using the product's metadata section in the Medusa admin. Since we're not using the standard attributes, we'll enhance the <code>ProductInfoTab</code> component to display any additional metadata we include.</p>
<p>By default, metadata is structured as an object. To make it simpler to create our list of attributes, we'll change it into an array. </p>
<p>In this case, we'll feature four attributes from the metadata, splitting them into two columns. If you want to show a different number of attributes, you can easily adjust the values within the <code>slice()</code> function as needed.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ... other components</span>

<span class="hljs-keyword">const</span> ProductInfoTab = <span class="hljs-function">(<span class="hljs-params">{ product }: ProductTabsProps</span>) =&gt;</span> {
  <span class="hljs-comment">// map the metadata object to an array</span>
  <span class="hljs-keyword">const</span> metadata = useMemo(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (!product.metadata) <span class="hljs-keyword">return</span> []
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">Object</span>.keys(product.metadata).map(<span class="hljs-function">(<span class="hljs-params">key</span>) =&gt;</span> {
      <span class="hljs-keyword">return</span> [key, product.metadata?.[key]]
    })
  }, [product])

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Tab.Panel</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-small-regular py-8"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid grid-cols-2 gap-x-8"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col gap-y-4"</span>&gt;</span>
                {/* Map the metadata as product information */}
          {metadata &amp;&amp;
            metadata.slice(0, 2).map(([key, value], i) =&gt; (
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{i}</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold"</span>&gt;</span>{key}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{value}<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col gap-y-4"</span>&gt;</span>
          {metadata.length &gt; 2 &amp;&amp;
            metadata.slice(2, 4).map(([key, value], i) =&gt; {
              return (
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{i}</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold"</span>&gt;</span>{key}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{value}<span class="hljs-tag">&lt;/<span class="hljs-name">p</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">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      {product.tags?.length ? (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold"</span>&gt;</span>Tags<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      ) : null}
    <span class="hljs-tag">&lt;/<span class="hljs-name">Tab.Panel</span>&gt;</span></span>
  )
}

<span class="hljs-comment">// ... other components</span>
</code></pre>
<h3 id="heading-how-to-adjust-the-shipping-details">How to Adjust the Shipping Details</h3>
<p>Shipping information isn't relevant for digital products, so we'll change the content in this tab. You can make any necessary adjustments to the content within the <code>ShippingInfoTab</code> component in the same file to better match your store's requirements.</p>
<pre><code class="lang-jaavscript">// ... other components

const ProductTabs = ({ product }: ProductTabsProps) =&gt; {
  const tabs = useMemo(() =&gt; {
    return [
      {
        label: "Product Information",
        component: &lt;ProductInfoTab product={product} /&gt;,
      },
      {
        label: "E-book delivery",
        component: &lt;ShippingInfoTab /&gt;,
      },
    ]
  }, [product])
    // ... rest of code
}

// ... other components

const ShippingInfoTab = () =&gt; {
  return (
    &lt;Tab.Panel className="text-small-regular py-8"&gt;
      &lt;div className="grid grid-cols-1 gap-y-8"&gt;
        &lt;div className="flex items-start gap-x-2"&gt;
          &lt;FastDelivery /&gt;
          &lt;div&gt;
            &lt;span className="font-semibold"&gt;Instant delivery&lt;/span&gt;
            &lt;p className="max-w-sm"&gt;
              Your e-book will be delivered instantly via email. You can also
              download it from your account anytime.
            &lt;/p&gt;
          &lt;/div&gt;
        &lt;/div&gt;
        &lt;div className="flex items-start gap-x-2"&gt;
          &lt;Refresh /&gt;
          &lt;div&gt;
            &lt;span className="font-semibold"&gt;Free previews&lt;/span&gt;
            &lt;p className="max-w-sm"&gt;
              Get a free preview of the e-book before you buy it. Just click the
              button above to download it.
            &lt;/p&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/Tab.Panel&gt;
  )
}

// ... other components
</code></pre>
<p>The <code>ProductTabs</code> component is used for rendering a set of tabs. The component takes a <code>product</code> prop, and it uses the <code>useMemo</code> hook to create an array of tab objects. Each tab object consists of a label and a component to be displayed when that tab is active. </p>
<p>In the above snippet, there are two tabs: "Product Information" and "E-book delivery." The "Product Information" tab displays information about the product using the <code>ProductInfoTab</code> component, which we defined earlier. </p>
<p>The "E-book delivery" tab uses the <code>ShippingInfoTab</code> component to display information related to e-book delivery. Inside the <code>ShippingInfoTab</code> component, it provides details about the delivery process, mentioning instant delivery via email and the option to download from an account, as well as free e-book previews.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/10p0z2piqkpv60m4jgab.png" alt="Image" width="600" height="400" loading="lazy">
<em>Product Page</em></p>
<h2 id="heading-how-to-simplify-the-checkout">How to Simplify the Checkout</h2>
<p>Selling digital products doesn't require gathering customers' physical addresses. We only need their first name and email address to deliver the e-book, making the checkout process simpler by removing unnecessary input fields. </p>
<p>In this example, we'll keep only the first name, last name, country, and email fields, completely removing the billing address section. Keep in mind that your specific requirements may require different input fields.</p>
<p>To start, we'll adjust the checkout types and context by removing any references to values that are no longer needed.</p>
<pre><code class="lang-javascript"><span class="hljs-string">"use client"</span>

<span class="hljs-keyword">import</span> { medusaClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@lib/config"</span>
<span class="hljs-keyword">import</span> useToggleState, { StateType } <span class="hljs-keyword">from</span> <span class="hljs-string">"@lib/hooks/use-toggle-state"</span>
<span class="hljs-keyword">import</span> { Cart, Customer, StorePostCartsCartReq } <span class="hljs-keyword">from</span> <span class="hljs-string">"@medusajs/medusa"</span>
<span class="hljs-keyword">import</span> Wrapper <span class="hljs-keyword">from</span> <span class="hljs-string">"@modules/checkout/components/payment-wrapper"</span>
<span class="hljs-keyword">import</span> { isEqual } <span class="hljs-keyword">from</span> <span class="hljs-string">"lodash"</span>
<span class="hljs-keyword">import</span> {
  formatAmount,
  useCart,
  useCartShippingOptions,
  useMeCustomer,
  useRegions,
  useSetPaymentSession,
  useUpdateCart,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"medusa-react"</span>
<span class="hljs-keyword">import</span> { useRouter } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/navigation"</span>
<span class="hljs-keyword">import</span> React, { createContext, useContext, useEffect, useMemo } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> { FormProvider, useForm, useFormContext } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-hook-form"</span>
<span class="hljs-keyword">import</span> { useStore } <span class="hljs-keyword">from</span> <span class="hljs-string">"./store-context"</span>

type AddressValues = {
  <span class="hljs-attr">first_name</span>: string
  <span class="hljs-attr">last_name</span>: string
  <span class="hljs-attr">country_code</span>: string
}

<span class="hljs-keyword">export</span> type CheckoutFormValues = {
  <span class="hljs-attr">shipping_address</span>: AddressValues
  billing_address?: AddressValues
  <span class="hljs-attr">email</span>: string
}

interface CheckoutContext {
  cart?: Omit&lt;Cart, <span class="hljs-string">"refundable_amount"</span> | <span class="hljs-string">"refunded_total"</span>&gt;
  shippingMethods: { label?: string; value?: string; price: string }[]
  <span class="hljs-attr">isLoading</span>: boolean
  <span class="hljs-attr">readyToComplete</span>: boolean
  <span class="hljs-attr">sameAsBilling</span>: StateType
  <span class="hljs-attr">editAddresses</span>: StateType
  <span class="hljs-attr">initPayment</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-keyword">void</span>&gt;
  setAddresses: <span class="hljs-function">(<span class="hljs-params">addresses: CheckoutFormValues</span>) =&gt;</span> <span class="hljs-keyword">void</span>
  <span class="hljs-attr">setSavedAddress</span>: <span class="hljs-function">(<span class="hljs-params">address: AddressValues</span>) =&gt;</span> <span class="hljs-keyword">void</span>
  <span class="hljs-attr">setShippingOption</span>: <span class="hljs-function">(<span class="hljs-params">soId: string</span>) =&gt;</span> <span class="hljs-keyword">void</span>
  <span class="hljs-attr">setPaymentSession</span>: <span class="hljs-function">(<span class="hljs-params">providerId: string</span>) =&gt;</span> <span class="hljs-keyword">void</span>
  <span class="hljs-attr">onPaymentCompleted</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">void</span>
}

<span class="hljs-keyword">const</span> CheckoutContext = createContext&lt;CheckoutContext | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>)
</code></pre>
<p>In the above snippet, you define TypeScript types for address values and the overall form structure. The <code>CheckoutContext</code> is also created to serve as a context for sharing checkout-related data and functions with other components.</p>
<pre><code class="lang-javascript">interface CheckoutProviderProps {
  children?: React.ReactNode
}

<span class="hljs-keyword">const</span> IDEMPOTENCY_KEY = <span class="hljs-string">"create_payment_session_key"</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CheckoutProvider = <span class="hljs-function">(<span class="hljs-params">{ children }: CheckoutProviderProps</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> {
    cart,
    setCart,
    <span class="hljs-attr">addShippingMethod</span>: {
      <span class="hljs-attr">mutate</span>: setShippingMethod,
      <span class="hljs-attr">isLoading</span>: addingShippingMethod,
    },
    <span class="hljs-attr">completeCheckout</span>: { <span class="hljs-attr">mutate</span>: complete, <span class="hljs-attr">isLoading</span>: completingCheckout },
  } = useCart()

  <span class="hljs-keyword">const</span> { customer } = useMeCustomer()
  <span class="hljs-keyword">const</span> { countryCode } = useStore()

  <span class="hljs-keyword">const</span> methods = useForm&lt;CheckoutFormValues&gt;({
    <span class="hljs-attr">defaultValues</span>: mapFormValues(customer, cart, countryCode),
    <span class="hljs-attr">reValidateMode</span>: <span class="hljs-string">"onChange"</span>,
  })
</code></pre>
<p>The <code>CheckoutProvider</code> component manages cart data, customer information, form handling, and interactions with payment and shipping methods. It sets up various hooks and functions for these purposes.</p>
<p>You also define an idempotency key which will be used for preventing duplicate requests during payment session creation.</p>
<pre><code class="lang-javascript"> <span class="hljs-keyword">const</span> methods = useForm&lt;CheckoutFormValues&gt;({
    <span class="hljs-attr">defaultValues</span>: mapFormValues(customer, cart, countryCode),
    <span class="hljs-attr">reValidateMode</span>: <span class="hljs-string">"onChange"</span>,
  })

  <span class="hljs-keyword">const</span> {
    <span class="hljs-attr">mutate</span>: setPaymentSessionMutation,
    <span class="hljs-attr">isLoading</span>: settingPaymentSession,
  } = useSetPaymentSession(cart?.id!)

  <span class="hljs-keyword">const</span> { <span class="hljs-attr">mutate</span>: updateCart, <span class="hljs-attr">isLoading</span>: updatingCart } = useUpdateCart(
    cart?.id!
  )

  <span class="hljs-keyword">const</span> { shipping_options } = useCartShippingOptions(cart?.id!, {
    <span class="hljs-attr">enabled</span>: !!cart?.id,
  })

  <span class="hljs-keyword">const</span> { regions } = useRegions()

  <span class="hljs-keyword">const</span> { resetCart, setRegion } = useStore()
  <span class="hljs-keyword">const</span> { push } = useRouter()

  <span class="hljs-keyword">const</span> editAddresses = useToggleState()
  <span class="hljs-keyword">const</span> sameAsBilling = useToggleState(
    cart?.billing_address &amp;&amp; cart?.shipping_address
      ? isEqual(cart.billing_address, cart.shipping_address)
      : <span class="hljs-literal">true</span>
  )
</code></pre>
<p>In this section of code, several variables and hooks are initialized to facilitate the management of a checkout process. </p>
<p>We use the <code>methods</code> variable to manage the checkout form, with initial values populated by the <code>mapFormValues</code> function. The code also sets up mutation functions for updating the payment session and the cart (<code>setPaymentSessionMutation</code> and <code>updateCart</code>) and tracks their loading states. It retrieves available shipping options and regions using hooks, and it also handles cart resets and region selection. </p>
<p>It also employs boolean states (<code>editAddresses</code> and <code>sameAsBilling</code>) to manage whether the user is currently editing addresses and whether the billing address matches the shipping address. </p>
<p>These components collectively ensure smooth navigation and data management in the checkout process.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/**
   * Boolean that indicates if a part of the checkout is loading.
   */</span>
  <span class="hljs-keyword">const</span> isLoading = useMemo(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> (
      addingShippingMethod ||
      settingPaymentSession ||
      updatingCart ||
      completingCheckout
    )
  }, [
    addingShippingMethod,
    completingCheckout,
    settingPaymentSession,
    updatingCart,
  ])

  <span class="hljs-comment">/**
   * Boolean that indicates if the checkout is ready to be completed. A checkout is ready to be completed if
   * the user has supplied a email, shipping address, billing address, shipping method, and a method of payment.
   */</span>
  <span class="hljs-keyword">const</span> readyToComplete = useMemo(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> (
      !!cart &amp;&amp;
      !!cart.email &amp;&amp;
      !!cart.shipping_address &amp;&amp;
      !!cart.billing_address &amp;&amp;
      !!cart.payment_session &amp;&amp;
      cart.shipping_methods?.length &gt; <span class="hljs-number">0</span>
    )
  }, [cart])

  <span class="hljs-keyword">const</span> shippingMethods = useMemo(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (shipping_options &amp;&amp; cart?.region) {
      <span class="hljs-keyword">return</span> shipping_options?.map(<span class="hljs-function">(<span class="hljs-params">option</span>) =&gt;</span> ({
        <span class="hljs-attr">value</span>: option.id,
        <span class="hljs-attr">label</span>: option.name,
        <span class="hljs-attr">price</span>: formatAmount({
          <span class="hljs-attr">amount</span>: option.amount || <span class="hljs-number">0</span>,
          <span class="hljs-attr">region</span>: cart.region,
        }),
      }))
    }

    <span class="hljs-keyword">return</span> []
  }, [shipping_options, cart])
</code></pre>
<p>In the code above, first the <code>isLoading</code> boolean is computed using the <code>useMemo</code> hook. It reflects whether any part of the checkout is in a loading state. </p>
<p>This is determined by observing four loading flags: <code>addingShippingMethod</code>, <code>settingPaymentSession</code>, <code>updatingCart</code>, and <code>completingCheckout</code>. If any of these flags is <code>true</code>, the <code>isLoading</code> flag will also be <code>true</code>. This indicates that some part of the checkout is currently in progress.</p>
<p>The <code>readyToComplete</code> boolean, also computed with <code>useMemo</code>, assesses whether the checkout is prepared for completion. </p>
<p>To be deemed ready, several conditions must be met: there must be a valid <code>cart</code> object, an email address, a shipping address, a billing address, a payment session, and at least one shipping method selected. If all these conditions are satisfied, <code>readyToComplete</code> will be <code>true</code>, signaling that the checkout process is set to be finalized.</p>
<p>Finally, the <code>shippingMethods</code> variable is computed using <code>useMemo</code>. It is an array of available shipping methods with associated information. It maps the <code>shipping_options</code> (if they exist) to an array of objects, each containing a <code>value</code>, <code>label</code>, and <code>price</code>. </p>
<p>These objects represent the shipping options, their names, and prices, formatted using the <code>formatAmount</code> function. This data is used to display and select shipping methods during the checkout process.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/**
   * Resets the form when the cart changed.
   */</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (cart?.id) {
      methods.reset(mapFormValues(customer, cart, countryCode))
    }
  }, [customer, cart, methods, countryCode])

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (!cart) {
      editAddresses.open()
      <span class="hljs-keyword">return</span>
    }

    <span class="hljs-keyword">if</span> (cart?.shipping_address &amp;&amp; cart?.billing_address) {
      editAddresses.close()
      <span class="hljs-keyword">return</span>
    }

    editAddresses.open()
    <span class="hljs-comment">// eslint-disable-next-line react-hooks/exhaustive-deps</span>
  }, [cart])

  <span class="hljs-comment">/**
   * Method to set the selected shipping method for the cart. This is called when the user selects a shipping method, such as UPS, FedEx, etc.
   */</span>
  <span class="hljs-keyword">const</span> setShippingOption = <span class="hljs-function">(<span class="hljs-params">soId: string</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (cart) {
      setShippingMethod(
        { <span class="hljs-attr">option_id</span>: soId },
        {
          <span class="hljs-attr">onSuccess</span>: <span class="hljs-function">(<span class="hljs-params">{ cart }</span>) =&gt;</span> setCart(cart),
        }
      )
    }
  }

  <span class="hljs-comment">/**
   * Method to create the payment sessions available for the cart. Uses a idempotency key to prevent duplicate requests.
   */</span>
  <span class="hljs-keyword">const</span> createPaymentSession = <span class="hljs-keyword">async</span> (cartId: string) =&gt; {
    <span class="hljs-keyword">return</span> medusaClient.carts
      .createPaymentSessions(cartId, {
        <span class="hljs-string">"Idempotency-Key"</span>: IDEMPOTENCY_KEY,
      })
      .then(<span class="hljs-function">(<span class="hljs-params">{ cart }</span>) =&gt;</span> cart)
      .catch(<span class="hljs-function">() =&gt;</span> <span class="hljs-literal">null</span>)
  }

  <span class="hljs-comment">/**
   * Method that calls the createPaymentSession method and updates the cart with the payment session.
   */</span>
  <span class="hljs-keyword">const</span> initPayment = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">if</span> (cart?.id &amp;&amp; !cart.payment_sessions?.length &amp;&amp; cart?.items?.length) {
      <span class="hljs-keyword">const</span> paymentSession = <span class="hljs-keyword">await</span> createPaymentSession(cart.id)

      <span class="hljs-keyword">if</span> (!paymentSession) {
        <span class="hljs-built_in">setTimeout</span>(initPayment, <span class="hljs-number">500</span>)
      } <span class="hljs-keyword">else</span> {
        setCart(paymentSession)
        <span class="hljs-keyword">return</span>
      }
    }
  }

  <span class="hljs-comment">/**
   * Method to set the selected payment session for the cart. This is called when the user selects a payment provider, such as Stripe, PayPal, etc.
   */</span>
  <span class="hljs-keyword">const</span> setPaymentSession = <span class="hljs-function">(<span class="hljs-params">providerId: string</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (cart) {
      setPaymentSessionMutation(
        {
          <span class="hljs-attr">provider_id</span>: providerId,
        },
        {
          <span class="hljs-attr">onSuccess</span>: <span class="hljs-function">(<span class="hljs-params">{ cart }</span>) =&gt;</span> {
            setCart(cart)
          },
        }
      )
    }
  }

  <span class="hljs-keyword">const</span> prepareFinalSteps = <span class="hljs-function">() =&gt;</span> {
    initPayment()

    <span class="hljs-keyword">if</span> (shippingMethods?.length &amp;&amp; shippingMethods?.[<span class="hljs-number">0</span>]?.value) {
      setShippingOption(shippingMethods[<span class="hljs-number">0</span>].value)
    }
  }

  <span class="hljs-keyword">const</span> setSavedAddress = <span class="hljs-function">(<span class="hljs-params">address: AddressValues</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> setValue = methods.setValue

    setValue(<span class="hljs-string">"shipping_address"</span>, {
      <span class="hljs-attr">country_code</span>: address.country_code || <span class="hljs-string">""</span>,
      <span class="hljs-attr">first_name</span>: address.first_name || <span class="hljs-string">""</span>,
      <span class="hljs-attr">last_name</span>: address.last_name || <span class="hljs-string">""</span>,
    })
  }

  <span class="hljs-comment">/**
   * Method that validates if the cart's region matches the shipping address's region. If not, it will update the cart region.
   */</span>
  <span class="hljs-keyword">const</span> validateRegion = <span class="hljs-function">(<span class="hljs-params">countryCode: string</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (regions &amp;&amp; cart) {
      <span class="hljs-keyword">const</span> region = regions.find(<span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span>
        r.countries.map(<span class="hljs-function">(<span class="hljs-params">c</span>) =&gt;</span> c.iso_2).includes(countryCode)
      )

      <span class="hljs-keyword">if</span> (region &amp;&amp; region.id !== cart.region.id) {
        setRegion(region.id, countryCode)
      }
    }
  }

  <span class="hljs-comment">/**
   * Method that sets the addresses and email on the cart.
   */</span>
  <span class="hljs-keyword">const</span> setAddresses = <span class="hljs-function">(<span class="hljs-params">data: CheckoutFormValues</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { shipping_address, billing_address, email } = data

    <span class="hljs-keyword">const</span> payload: StorePostCartsCartReq = {
      shipping_address,
      email,
    }

    <span class="hljs-keyword">if</span> (isEqual(shipping_address, billing_address)) {
      sameAsBilling.open()
    }

    <span class="hljs-keyword">if</span> (sameAsBilling.state) {
      payload.billing_address = shipping_address
    } <span class="hljs-keyword">else</span> {
      payload.billing_address = billing_address
    }

    updateCart(payload, {
      <span class="hljs-attr">onSuccess</span>: <span class="hljs-function">(<span class="hljs-params">{ cart }</span>) =&gt;</span> {
        setCart(cart)
        prepareFinalSteps()
      },
    })
  }

  <span class="hljs-comment">/**
   * Method to complete the checkout process. This is called when the user clicks the "Complete Checkout" button.
   */</span>
  <span class="hljs-keyword">const</span> onPaymentCompleted = <span class="hljs-function">() =&gt;</span> {
    complete(<span class="hljs-literal">undefined</span>, {
      <span class="hljs-attr">onSuccess</span>: <span class="hljs-function">(<span class="hljs-params">{ data }</span>) =&gt;</span> {
        resetCart()
        push(<span class="hljs-string">`/order/confirmed/<span class="hljs-subst">${data.id}</span>`</span>)
      },
    })
  }

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">FormProvider</span> {<span class="hljs-attr">...methods</span>}&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">CheckoutContext.Provider</span>
        <span class="hljs-attr">value</span>=<span class="hljs-string">{{</span>
          <span class="hljs-attr">cart</span>,
          <span class="hljs-attr">shippingMethods</span>,
          <span class="hljs-attr">isLoading</span>,
          <span class="hljs-attr">readyToComplete</span>,
          <span class="hljs-attr">sameAsBilling</span>,
          <span class="hljs-attr">editAddresses</span>,
          <span class="hljs-attr">initPayment</span>,
          <span class="hljs-attr">setAddresses</span>,
          <span class="hljs-attr">setSavedAddress</span>,
          <span class="hljs-attr">setShippingOption</span>,
          <span class="hljs-attr">setPaymentSession</span>,
          <span class="hljs-attr">onPaymentCompleted</span>,
        }}
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Wrapper</span> <span class="hljs-attr">paymentSession</span>=<span class="hljs-string">{cart?.payment_session}</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">Wrapper</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">CheckoutContext.Provider</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">FormProvider</span>&gt;</span></span>
  )
}
</code></pre>
<p>This code section orchestrates various aspects of an e-commerce checkout process. It manages form state, resets the form when the cart changes, and toggles address editing visibility. It handles the selection of shipping methods, the creation and initialization of payment sessions, and the choice of payment providers. And it ensures that shipping addresses, billing addresses, and email information are set appropriately, and validates the cart's region based on the shipping address. </p>
<p>It also coordinates the completion of the checkout process, including payment processing and order confirmation. </p>
<p>All of these functions and data are encapsulated within the <code>CheckoutProvider</code> component.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useCheckout = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> context = useContext(CheckoutContext)
  <span class="hljs-keyword">const</span> form = useFormContext&lt;CheckoutFormValues&gt;()
  <span class="hljs-keyword">if</span> (context === <span class="hljs-literal">null</span>) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(
      <span class="hljs-string">"useProductActionContext must be used within a ProductActionProvider"</span>
    )
  }
  <span class="hljs-keyword">return</span> { ...context, ...form }
}

<span class="hljs-comment">/**
 * Method to map the fields of a potential customer and the cart to the checkout form values. Information is assigned with the following priority:
 * 1. Cart information
 * 2. Customer information
 * 3. Default values - null
 */</span>
<span class="hljs-keyword">const</span> mapFormValues = (
  customer?: Omit&lt;Customer, <span class="hljs-string">"password_hash"</span>&gt;,
  cart?: Omit&lt;Cart, <span class="hljs-string">"refundable_amount"</span> | <span class="hljs-string">"refunded_total"</span>&gt;,
  currentCountry?: string
): <span class="hljs-function"><span class="hljs-params">CheckoutFormValues</span> =&gt;</span> {
  <span class="hljs-keyword">const</span> customerShippingAddress = customer?.shipping_addresses?.[<span class="hljs-number">0</span>]
  <span class="hljs-keyword">const</span> customerBillingAddress = customer?.billing_address

  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">shipping_address</span>: {
      <span class="hljs-attr">first_name</span>:
        cart?.shipping_address?.first_name ||
        customerShippingAddress?.first_name ||
        <span class="hljs-string">""</span>,
      <span class="hljs-attr">last_name</span>:
        cart?.shipping_address?.last_name ||
        customerShippingAddress?.last_name ||
        <span class="hljs-string">""</span>,
      <span class="hljs-attr">country_code</span>:
        currentCountry ||
        cart?.shipping_address?.country_code ||
        customerShippingAddress?.country_code ||
        <span class="hljs-string">""</span>,
    },
    <span class="hljs-attr">billing_address</span>: {
      <span class="hljs-attr">first_name</span>:
        cart?.billing_address?.first_name ||
        customerBillingAddress?.first_name ||
        <span class="hljs-string">""</span>,
      <span class="hljs-attr">last_name</span>:
        cart?.billing_address?.last_name ||
        customerBillingAddress?.last_name ||
        <span class="hljs-string">""</span>,
      <span class="hljs-attr">country_code</span>:
        cart?.shipping_address?.country_code ||
        customerBillingAddress?.country_code ||
        <span class="hljs-string">""</span>,
    },
    <span class="hljs-attr">email</span>: cart?.email || customer?.email || <span class="hljs-string">""</span>,
  }
}
</code></pre>
<p>The <code>useCheckout</code> hook is used to access the checkout context and form context, typically used in React components. It retrieves the <code>CheckoutContext</code> from the context of the application, and it also gets the form context of the checkout form, allowing components to access and utilize these contexts.</p>
<p>The <code>mapFormValues</code> function is responsible for mapping and prioritizing information for the checkout form. It takes customer and cart data, along with the current country, and generates values for the checkout form fields. </p>
<p>It prioritizes data in this order: 1) Cart information, 2) Customer information, and 3) Default values set to null if no information is available. This function helps populate the checkout form with the most relevant data, ensuring a smoother user experience during the checkout process.</p>
<p>Now that the context is updated, we’ll remove the redundant input fields from the checkout form.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useCheckout } <span class="hljs-keyword">from</span> <span class="hljs-string">"@lib/context/checkout-context"</span>
<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">"@modules/common/components/button"</span>
<span class="hljs-keyword">import</span> Spinner <span class="hljs-keyword">from</span> <span class="hljs-string">"@modules/common/icons/spinner"</span>
<span class="hljs-keyword">import</span> ShippingAddress <span class="hljs-keyword">from</span> <span class="hljs-string">"../shipping-address"</span>

<span class="hljs-keyword">const</span> Addresses = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> {
    <span class="hljs-attr">editAddresses</span>: { <span class="hljs-attr">state</span>: isEdit, <span class="hljs-attr">toggle</span>: setEdit },
    setAddresses,
    handleSubmit,
    cart,
  } = useCheckout()
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl-semi flex items-center gap-x-4 px-8 pb-6 pt-8"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-gray-900 w-8 h-8 rounded-full text-white flex justify-center items-center text-sm"</span>&gt;</span>
          1
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>Shipping address<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      {isEdit ? (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-8 pb-8"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ShippingAddress</span> /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Button</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-[200px] mt-6"</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleSubmit(setAddresses)}</span>
          &gt;</span>
            Continue to delivery
          <span class="hljs-tag">&lt;/<span class="hljs-name">Button</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">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-gray-50 px-8 py-6 text-small-regular"</span>&gt;</span>
            {cart &amp;&amp; cart.shipping_address ? (
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-start gap-x-8"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-green-400 rounded-full min-w-[24px] h-6 flex items-center justify-center text-white text-small-regular"</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-start justify-between w-full"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>
                      {cart.shipping_address.first_name}{" "}
                      {cart.shipping_address.last_name}
                      {cart.shipping_address.country}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-4 flex flex-col"</span>&gt;</span>
                      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{cart.email}<span class="hljs-tag">&lt;/<span class="hljs-name">span</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">div</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">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{setEdit}</span>&gt;</span>Edit<span class="hljs-tag">&lt;/<span class="hljs-name">button</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">div</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">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">""</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">Spinner</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">div</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">div</span>&gt;</span></span>
  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Addresses
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/t1osg7g86bmd4s8qugdf.png" alt="Image" width="600" height="400" loading="lazy">
<em>Checkout Page</em></p>
<p>In the last step, we'll modify the <code>shipping-details</code> component to show important information after the order is successfully placed. In this situation, we'll remove any extra details and add the buyer's email address for reference.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Address, ShippingMethod } <span class="hljs-keyword">from</span> <span class="hljs-string">"@medusajs/medusa"</span>

type ShippingDetailsProps = {
  <span class="hljs-attr">address</span>: Address
  <span class="hljs-attr">shippingMethods</span>: ShippingMethod[]
  <span class="hljs-attr">email</span>: string
}

<span class="hljs-keyword">const</span> ShippingDetails = <span class="hljs-function">(<span class="hljs-params">{
  address,
  shippingMethods,
  email,
}: ShippingDetailsProps</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-base-regular"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-base-semi"</span>&gt;</span>Delivery<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"my-2"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-small-regular text-gray-700"</span>&gt;</span>Details<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{`${address.first_name} ${address.last_name}`}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{email}<span class="hljs-tag">&lt;/<span class="hljs-name">span</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">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"my-2"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-small-regular text-gray-700"</span>&gt;</span>Delivery method<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
          {shippingMethods.map((sm) =&gt; {
            return <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{sm.id}</span>&gt;</span>{sm.shipping_option.name}<span class="hljs-tag">&lt;/<span class="hljs-name">div</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">div</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> ShippingDetails
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/io66rfgqkr88i1wcojj9.png" alt="Image" width="600" height="400" loading="lazy">
<em>Order Confirmation Page</em></p>
<h2 id="heading-how-to-deliver-digital-products">How to Deliver Digital Products</h2>
<p>There are various ways to get digital products to customers, like sending a download link by email, adding a download button on the order confirmation page, or giving access through their account.</p>
<p>In all these situations, our main goal is to confirm that only those who've purchased the product can get it. </p>
<p>To do this, I've set up the backend to create a special code (token) for each digital item in an order. We can use GET <code>/store/:token</code> to check the token and give the file to the user. But this method shows the file's web address to the user, which isn't great for preventing piracy. </p>
<p>So we are going to make a Next API route at <code>src/app/api/download/main/[token]/route.ts</code>. This route will handle the token, acting as a middleman to provide the file to the user without revealing where it's stored.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { NextRequest, NextResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/server"</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">GET</span>(<span class="hljs-params">
  req: NextRequest,
  { params }: { params: Record&lt;string, any&gt; }
</span>) </span>{
  <span class="hljs-comment">// Get the token from the URL</span>
  <span class="hljs-keyword">const</span> { token } = params

  <span class="hljs-comment">// Define the URL to fetch the PDF file data from</span>
  <span class="hljs-keyword">const</span> pdfUrl = <span class="hljs-string">`<span class="hljs-subst">${process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL}</span>/store/product-media/<span class="hljs-subst">${token}</span>`</span>

  <span class="hljs-comment">// Fetch the PDF file data</span>
  <span class="hljs-keyword">const</span> { file, filename } = <span class="hljs-keyword">await</span> fetch(pdfUrl).then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> res.json())

  <span class="hljs-comment">// Handle the case where the token is invalid</span>
  <span class="hljs-keyword">if</span> (!file) <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> NextResponse(<span class="hljs-string">"Invalid token"</span>, { <span class="hljs-attr">status</span>: <span class="hljs-number">401</span> })

  <span class="hljs-comment">// Fetch the PDF file</span>
  <span class="hljs-keyword">const</span> pdfResponse = <span class="hljs-keyword">await</span> fetch(file)

  <span class="hljs-comment">// Handle the case where the PDF could not be fetched</span>
  <span class="hljs-keyword">if</span> (!pdfResponse.ok) <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> NextResponse(<span class="hljs-string">"PDF not found"</span>, { <span class="hljs-attr">status</span>: <span class="hljs-number">404</span> })

  <span class="hljs-comment">// Get the PDF content as a buffer</span>
  <span class="hljs-keyword">const</span> pdfBuffer = <span class="hljs-keyword">await</span> pdfResponse.arrayBuffer()

  <span class="hljs-comment">// Define response headers</span>
  <span class="hljs-keyword">const</span> headers = {
    <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/pdf"</span>,
    <span class="hljs-string">"Content-Disposition"</span>: <span class="hljs-string">`attachment; filename="<span class="hljs-subst">${filename}</span>"`</span>, <span class="hljs-comment">// This sets the file name for the download</span>
  }

  <span class="hljs-comment">// Create a NextResponse with the PDF content and headers</span>
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">new</span> NextResponse(pdfBuffer, {
    <span class="hljs-attr">status</span>: <span class="hljs-number">200</span>,
    headers,
  })

  <span class="hljs-keyword">return</span> response
}
</code></pre>
<p>This code defines a serverless function for handling HTTP GET requests in a Next.js application. It retrieves a PDF file using a token provided in the URL parameters, fetching the file from an external source. The function ensures the token's validity and the availability of the PDF file. If the token is invalid, it returns a "401 Unauthorized" response. If the PDF is not found, it returns a "404 Not Found" response. </p>
<p>When the PDF is successfully fetched, it constructs response headers, including the content type as "application/pdf" and a suggested filename for download, and returns the PDF file to the client as a downloadable attachment. This code is typically used to serve PDF files in response to specific GET requests.</p>
<p>We can now link to this API route from the delivery email like this: <code>{your_store_url}/api/download/main/{token}</code>.</p>
<p>You can add your own logic to invalidate tokens after a certain time or X number of downloads.</p>
<h2 id="heading-mission-accomplished"><strong>Mission Accomplished!</strong></h2>
<p>Congratulations, you've made it! Don't forget to explore more <a target="_blank" href="https://docs.medusajs.com/recipes">Recipes</a> for further ways to make the most of Medusa.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use the JSON Module in Python – A Beginner's Guide ]]>
                </title>
                <description>
                    <![CDATA[ JSON (JavaScript Object Notation) is a popular, lightweight data interchange standard. It represents data structures made up of key-value pairs that's quite straightforward and human-readable.  JSON has become the industry standard for data interchan... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-the-json-module-in-python/</link>
                <guid isPermaLink="false">66ba0ea7d14c87384322b695</guid>
                
                    <category>
                        <![CDATA[ json ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Mon, 05 Jun 2023 22:51:24 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/06/json-module.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>JSON (JavaScript Object Notation) is a popular, lightweight data interchange standard. It represents data structures made up of key-value pairs that's quite straightforward and human-readable. </p>
<p>JSON has become the industry standard for data interchange between online services. And it's widely utilized in modern programming languages, including Python.</p>
<p>JSON data is frequently expressed as nested dictionaries, lists, and scalar values such as texts, numbers, booleans, and null. It is named JSON because it closely mimics the syntax used in JavaScript objects.</p>
<p>In this tutorial, you will explore the JSON module in Python and learn how to effectively work with JSON data.</p>
<h2 id="heading-pythons-built-in-json-module">Python's Built-in JSON Module</h2>
<p>JSON plays an important role in Python programming because it allows efficient data serialization and deserialization. It enables Python programs to effortlessly communicate with web services, exchange data, and store structured information. </p>
<p>Developers can use JSON to seamlessly link their Python programs with a variety of APIs, databases, and external systems that use JSON for data representation.</p>
<p>If you're looking to learn how to interact with web services using Python, check out <a target="_blank" href="https://blog.ashutoshkrris.in/how-to-interact-with-web-services-using-python">my tutorial on the requests module</a>.</p>
<p>The built-in JSON module in Python provides a powerful set of methods and classes that make working with JSON data simple. Developers can use it to encode Python objects into JSON strings and decode JSON strings back into Python objects.</p>
<h2 id="heading-how-to-store-json-data-in-a-file">How to Store JSON Data in a File</h2>
<p>When working with JSON data in Python, you'll often need to save the data or share it with others. Storing JSON data in a file enables quick retrieval and data persistence. </p>
<p>In this section, you'll learn how to use Python's <code>json.dump()</code> function to save JSON data to a file. This process involves serializing the JSON data and saving it to a file, which you can subsequently read and use as needed.</p>
<h3 id="heading-the-jsondump-function">The <code>json.dump()</code> function</h3>
<p>The <code>json.dump()</code> function in Python allows you to store JSON data directly into a file. This function takes two parameters: the data to be serialized and the file object where the data will be written.</p>
<p>To write JSON data to a file, you need to follow a few steps. First, you need to open a file in write mode, specifying the file path. Then, you can use the <code>json.dump()</code> function to serialize the data and write it to the file. Finally, you need to close the file to ensure that all the data is properly saved.</p>
<p>Let's learn how to store data in a file using the horoscope API response as an example.</p>
<p>Assume you have made a GET request to the following URL: <a target="_blank" href="https://horoscope-app-api.vercel.app/api/v1/get-horoscope/daily?sign=capricorn&amp;day=today">https://horoscope-app-api.vercel.app/api/v1/get-horoscope/daily?sign=capricorn&amp;day=today</a>, which provides the daily horoscope for the Capricorn sign.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> json

<span class="hljs-comment"># Make the GET request to the horoscope API</span>
response = requests.get(<span class="hljs-string">"https://horoscope-app-api.vercel.app/api/v1/get-horoscope/daily?sign=capricorn&amp;day=today"</span>)
data = response.json()  <span class="hljs-comment"># Convert the response to JSON</span>

<span class="hljs-comment"># Store the JSON data in a file</span>
<span class="hljs-keyword">with</span> open(<span class="hljs-string">"horoscope_data.json"</span>, <span class="hljs-string">"w"</span>) <span class="hljs-keyword">as</span> file:
    json.dump(data, file)

print(<span class="hljs-string">"Data stored successfully!"</span>)
</code></pre>
<p>In the code above, you use the <code>requests</code> library to make a GET request to the <a target="_blank" href="https://blog.ashutoshkrris.in/how-to-create-a-horoscope-api-with-beautiful-soup-and-flask">Horoscope API</a>. You then extract the JSON data from the response using the <code>.json()</code> method. Finally, you open a file named <code>horoscope_data.json</code> in write mode using the <code>with</code> statement, and you use <code>json.dump()</code> to store the data in the file.</p>
<p>Check out <a target="_blank" href="https://blog.ashutoshkrris.in/how-to-know-your-horoscope-using-python">this tutorial</a> to learn how to find out your horoscope using Python.</p>
<p>If you open the <code>horoscope_data.json</code> file, you'll see contents similar to below:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"data"</span>: {
    <span class="hljs-attr">"date"</span>: <span class="hljs-string">"Jun 3, 2023"</span>,
    <span class="hljs-attr">"horoscope_data"</span>: <span class="hljs-string">"The forecast today is stormy. You may have sensed that there was some tension clouding the conversation at home. Resentments were left unsaid and subtle power games were played without resolution. Today, Capricorn, it all becomes too unbearable for you. Regardless of the risks involved, you will take measures to clear things up."</span>
  },
  <span class="hljs-attr">"status"</span>: <span class="hljs-number">200</span>,
  <span class="hljs-attr">"success"</span>: <span class="hljs-literal">true</span>
}
</code></pre>
<h2 id="heading-how-to-retrieve-data-from-a-json-file">How to Retrieve Data from a JSON File</h2>
<p>You'll often need to read data from a JSON file. For example, you may need to read configuration settings from a JSON file. Python's JSON module provides the <code>json.load()</code> function, which allows you to read and deserialize JSON data from a file. </p>
<p>In this section, you will learn how to use the <code>json.load()</code> function to retrieve JSON data from a file and work with it in your Python programs.</p>
<h3 id="heading-the-jsonload-function">The <code>json.load()</code> function</h3>
<p>The <code>json.load()</code> function accepts a file object as an argument and returns deserialized JSON data in the form of Python objects such as dictionaries, lists, strings, numbers, booleans, and null values.</p>
<p>To read JSON data from a file, you need to open the file in read mode, extract the data using the <code>json.load()</code> function, and store it in a variable for further processing. It's important to ensure that the file being read contains valid JSON data – otherwise, it may raise an exception.</p>
<p>Let's see how you can retrieve the data from the previously created <code>horoscope_data.json</code> file:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json

<span class="hljs-comment"># Retrieve JSON data from the file</span>
<span class="hljs-keyword">with</span> open(<span class="hljs-string">"horoscope_data.json"</span>, <span class="hljs-string">"r"</span>) <span class="hljs-keyword">as</span> file:
    data = json.load(file)

<span class="hljs-comment"># Access and process the retrieved JSON data</span>
date = data[<span class="hljs-string">"data"</span>][<span class="hljs-string">"date"</span>]
horoscope_data = data[<span class="hljs-string">"data"</span>][<span class="hljs-string">"horoscope_data"</span>]

<span class="hljs-comment"># Print the retrieved data</span>
print(<span class="hljs-string">f"Horoscope for date <span class="hljs-subst">{date}</span>: <span class="hljs-subst">{horoscope_data}</span>"</span>)
</code></pre>
<p>In the code above, you open the file <code>horoscope_data.json</code> in read mode using the <code>with</code> statement. You then use the <code>json.load()</code> function to deserialize the JSON data from the file into the data variable. Finally, you access specific fields of the JSON data (e.g., "date" and "horoscope_data") and process them as needed.</p>
<h2 id="heading-how-to-format-the-json-output">How to Format the JSON Output</h2>
<p>When you read data from a JSON file and print it, the output is displayed as a single line, which may not resemble the structured format of JSON.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json

<span class="hljs-comment"># Retrieve JSON data from the file</span>
<span class="hljs-keyword">with</span> open(<span class="hljs-string">"horoscope_data.json"</span>, <span class="hljs-string">"r"</span>) <span class="hljs-keyword">as</span> file:
    data = json.load(file)

print(data)
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">{<span class="hljs-string">'data'</span>: {<span class="hljs-string">'date'</span>: <span class="hljs-string">'Jun 3, 2023'</span>, <span class="hljs-string">'horoscope_data'</span>: <span class="hljs-string">'The forecast today is stormy. You may have sensed that there was some tension clouding the conversation at home. Resentments were left unsaid and subtle power games were played without resolution. Today, Capricorn, it all becomes too unbearable for you. Regardless of the risks involved, you will take measures to clear things up.'</span>}, <span class="hljs-string">'status'</span>: 200, <span class="hljs-string">'success'</span>: True}
</code></pre>
<h3 id="heading-the-jsondumps-function">The <code>json.dumps()</code> function</h3>
<p>The JSON module provides you with a <code>json.dumps()</code> function to serialize Python objects into a JSON formatted string. It provides various options for customization, including formatting the output to make it more human-readable.</p>
<p>The <code>json.dumps()</code> function provides several <a target="_blank" href="https://docs.python.org/3/library/json.html#json.dumps">options</a> to customize the output. The most commonly used is the <code>indent</code> which allows you to specify the number of spaces used for indentation.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json

<span class="hljs-comment"># Retrieve JSON data from the file</span>
<span class="hljs-keyword">with</span> open(<span class="hljs-string">"horoscope_data.json"</span>, <span class="hljs-string">"r"</span>) <span class="hljs-keyword">as</span> file:
    data = json.load(file)

<span class="hljs-comment"># Format the data</span>
formatted_data = json.dumps(data, indent=<span class="hljs-number">2</span>)

print(formatted_data)
</code></pre>
<p>Output:</p>
<pre><code class="lang-bash">{
  <span class="hljs-string">"data"</span>: {
    <span class="hljs-string">"date"</span>: <span class="hljs-string">"Jun 3, 2023"</span>,
    <span class="hljs-string">"horoscope_data"</span>: <span class="hljs-string">"The forecast today is stormy. You may have sensed that there was some tension clouding the conversation at home. Resentments were left unsaid and subtle power games were played without resolution. Today, Capricorn, it all becomes too unbearable for you. Regardless of the risks involved, you will take measures to clear things up."</span>
  },
  <span class="hljs-string">"status"</span>: 200,
  <span class="hljs-string">"success"</span>: <span class="hljs-literal">true</span>
}
</code></pre>
<p>As you can see, the JSON data is now formatted with proper indentation, enhancing its readability. This technique can be applied to any JSON data, allowing you to present JSON output in a more organized and visually appealing way.</p>
<h2 id="heading-the-jsontool-command-line-tool">The <code>json.tool</code> Command Line Tool</h2>
<p>Python's JSON module provides a convenient command line tool called <code>json.tool</code> that allows you to format and pretty-print JSON data directly from the command line. It is a useful utility for quickly visualizing the structure and contents of JSON data in a more readable and organized format.</p>
<p>To use <code>json.tool</code>, you can execute the following command in your command-line interface:</p>
<pre><code class="lang-bash">python -m json.tool &lt;input_file&gt; &lt;output_file&gt;
</code></pre>
<p>where:</p>
<ul>
<li><code>python -m json.tool</code> invokes the <code>json.tool</code> module using the Python interpreter.</li>
<li><code>&lt;input_file&gt;</code> represents the path to the JSON file you want to format.</li>
<li><code>&lt;output_file&gt;</code> is an optional argument that specifies the file to which you want to save the formatted JSON output. If not provided, the formatted output will be displayed on the console.</li>
</ul>
<p>Let's say you have a <code>horoscope_data.json</code> file with the following contents:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"data"</span>: {
    <span class="hljs-attr">"date"</span>: <span class="hljs-string">"Jun 3, 2023"</span>,
    <span class="hljs-attr">"horoscope_data"</span>: <span class="hljs-string">"The forecast today is stormy. You may have sensed that there was some tension clouding the conversation at home. Resentments were left unsaid and subtle power games were played without resolution. Today, Capricorn, it all becomes too unbearable for you. Regardless of the risks involved, you will take measures to clear things up."</span>
  },
  <span class="hljs-attr">"status"</span>: <span class="hljs-number">200</span>,
  <span class="hljs-attr">"success"</span>: <span class="hljs-literal">true</span>
}
</code></pre>
<p>Notice that the above JSON file has an indentation of two spaces.</p>
<p>To pretty-print this JSON file using <code>json.tool</code>, you can execute the following command:</p>
<pre><code class="lang-bash">python -m json.tool horoscope_data.json
</code></pre>
<p>The output will be:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"data"</span>: {
        <span class="hljs-attr">"date"</span>: <span class="hljs-string">"Jun 3, 2023"</span>,
        <span class="hljs-attr">"horoscope_data"</span>: <span class="hljs-string">"The forecast today is stormy. You may have sensed that there was some tension clouding the conversation at home. Resentments were left unsaid and subtle power games were played without resolution. Today, Capricorn, it all becomes too unbearable for you. Regardless of the risks involved, you will take measures to clear things up."</span>
    },
    <span class="hljs-attr">"status"</span>: <span class="hljs-number">200</span>,
    <span class="hljs-attr">"success"</span>: <span class="hljs-literal">true</span>
}
</code></pre>
<p>As you can see in the example, executing the <code>json.tool</code> module with the input file path formats the JSON data and displays the formatted output on the console.</p>
<p>You can also redirect the formatted output to an output file by specifying the output file name as the second argument:</p>
<pre><code class="lang-bash">python -m json.tool horoscope_data.json formatted_data.json
</code></pre>
<p>This command formats the JSON data from <code>horoscope_data.json</code> and saves the formatted output to <code>formatted_data.json</code>.</p>
<h2 id="heading-json-encoding-custom-objects">JSON Encoding Custom Objects</h2>
<p>The JSON module in Python allows you to encode and decode custom objects by using JSON encoder and decoder classes. You can define custom serialization and deserialization logic for your objects using these classes.</p>
<p><code>JSONEncoder</code> class allows you to customize the encoding process. To define how your custom object should be encoded into JSON format, you can extend the <code>JSONEncoder</code> and change its <code>default</code> method.</p>
<p>Here's an example of how you can extend the <code>JSONEncoder</code> class and customize the encoding process for a custom object:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Person</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, name, age</span>):</span>
        self.name = name
        self.age = age


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PersonEncoder</span>(<span class="hljs-params">json.JSONEncoder</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">default</span>(<span class="hljs-params">self, obj</span>):</span>
        <span class="hljs-keyword">if</span> isinstance(obj, Person):
            <span class="hljs-keyword">return</span> {<span class="hljs-string">"name"</span>: obj.name, <span class="hljs-string">"age"</span>: obj.age}
        <span class="hljs-keyword">return</span> super().default(obj)


<span class="hljs-comment"># Create a custom object</span>
person = Person(<span class="hljs-string">"Ashutosh Krishna"</span>, <span class="hljs-number">23</span>)

<span class="hljs-comment"># Encode the custom object using the custom encoder</span>
json_str = json.dumps(person, cls=PersonEncoder)

<span class="hljs-comment"># Print the encoded JSON string</span>
print(json_str)
</code></pre>
<p>In this example, you define a custom class <code>Person</code> with <code>name</code> and <code>age</code> attributes. You then create a subclass of <code>JSONEncoder</code> called <code>PersonEncoder</code> and override its <code>default</code> method. Within the <code>default</code> method, you check if the object being encoded is an instance of <code>Person</code>. If it is, you provide a JSON-serializable representation of the object by returning a dictionary containing the <code>name</code> and <code>age</code> attributes. If the object is not of type <code>Person</code>, you call the <code>default</code> method of the superclass to handle other types.</p>
<p>By using <code>json.dumps</code> and specifying the <code>cls</code> parameter as your custom encoder class <code>PersonEncoder</code>, you can encode the <code>person</code> object into a JSON string. The output will be:</p>
<pre><code class="lang-bash">{<span class="hljs-string">"name"</span>: <span class="hljs-string">"Ashutosh Krishna"</span>, <span class="hljs-string">"age"</span>: 23}
</code></pre>
<p>Similarly, you can specify custom decoding logic in the JSON decoder class, <code>JSONDecoder</code>. To define how JSON data should be decoded into your custom object, extend the <code>JSONDecoder</code> and override its <code>object_hook</code> function.</p>
<h2 id="heading-how-to-create-json-from-a-python-dictionary">How to Create JSON from a Python Dictionary</h2>
<p>You can use the <code>json.dumps()</code> function provided by the JSON module to create JSON from a <a target="_blank" href="https://blog.ashutoshkrris.in/everything-you-need-to-know-about-python-dictionaries">Python dictionary</a>. This function takes a Python object, typically a dictionary, and converts it into a JSON string representation.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json

<span class="hljs-comment"># Python dictionary</span>
data = {
    <span class="hljs-string">"name"</span>: <span class="hljs-string">"Ashutosh Krishna"</span>,
    <span class="hljs-string">"age"</span>: <span class="hljs-number">23</span>,
    <span class="hljs-string">"email"</span>: <span class="hljs-string">"ashutosh@example.com"</span>
}

<span class="hljs-comment"># Convert dictionary to JSON string</span>
json_str = json.dumps(data)

<span class="hljs-comment"># Print the JSON string</span>
print(json_str)
</code></pre>
<p>In this example, you have a Python dictionary <code>data</code> representing some data. By calling <code>json.dumps(data)</code>, you convert the dictionary into a JSON string. The output will be:</p>
<pre><code class="lang-bash">{<span class="hljs-string">"name"</span>: <span class="hljs-string">"Ashutosh Krishna"</span>, <span class="hljs-string">"age"</span>: 23, <span class="hljs-string">"email"</span>: <span class="hljs-string">"ashutosh@example.com"</span>}
</code></pre>
<h2 id="heading-how-to-create-a-python-dictionary-from-json">How to Create a Python Dictionary from JSON</h2>
<p>To create a Python dictionary from JSON data, you can use the <code>json.loads()</code> function provided by the JSON module. This function takes a JSON string and converts it into a corresponding Python object, typically a dictionary.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json

<span class="hljs-comment"># JSON string</span>
json_str = <span class="hljs-string">'{"name": "Ashutosh Krishna", "age": 23, "email": "ashutosh@example.com"}'</span>

<span class="hljs-comment"># Convert JSON string to Python dictionary</span>
data = json.loads(json_str)

<span class="hljs-comment"># Access the dictionary values</span>
print(data[<span class="hljs-string">"name"</span>])
print(data[<span class="hljs-string">"age"</span>])
print(data[<span class="hljs-string">"email"</span>])
</code></pre>
<p>In this example, you have a JSON string <code>json_str</code> representing some data. By calling <code>json.loads(json_str)</code>, you convert the JSON string into a Python dictionary. You can then access the values in the dictionary using their respective keys.</p>
<p>The output will be:</p>
<pre><code class="lang-bash">Ashutosh Krishna
23
ashutosh@example.com
</code></pre>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>Understanding the Python JSON module is necessary for working with JSON data because it is widely used for data exchange and storage in a variety of applications. </p>
<p>You can efficiently handle JSON data, interface with APIs, and deal with configuration files if you learn how to use the JSON module.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ A Beginner's Guide to the Strategy Design Pattern ]]>
                </title>
                <description>
                    <![CDATA[ The Strategy Design Pattern is a behavioral design pattern. It allows you to dynamically change the behavior of an object by encapsulating it into different strategies.  This pattern enables an object to choose from multiple algorithms and behaviors ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/a-beginners-guide-to-the-strategy-design-pattern/</link>
                <guid isPermaLink="false">66ba0e61228e16bed602a88f</guid>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Java ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Thu, 04 May 2023 17:43:03 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/05/strategy-design-pattern.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The Strategy Design Pattern is a behavioral design pattern. It allows you to dynamically change the behavior of an object by encapsulating it into different strategies. </p>
<p>This pattern enables an object to choose from multiple algorithms and behaviors at runtime, rather than statically choosing a single one.</p>
<p>It is based on the principle of composition over inheritance. It defines a family of algorithms, encapsulates each one, and makes them interchangeable at runtime. The core idea behind this pattern is to separate the algorithms from the main object. This allows the object to delegate the algorithm's behavior to one of its contained strategies.</p>
<p>In simpler terms, the Strategy Design Pattern provides a way to extract the behavior of an object into separate classes that can be swapped in and out at runtime. This enables the object to be more flexible and reusable, as different strategies can be easily added or modified without changing the object's core code.</p>
<h2 id="heading-benefits-of-using-the-strategy-design-pattern">Benefits of Using the Strategy Design Pattern</h2>
<p>Using the Strategy Design Pattern can provide several benefits, including:</p>
<ol>
<li><strong>Improved code flexibility</strong>: By encapsulating the behavior of an object into different strategies, the code becomes more flexible and easier to modify.</li>
<li><strong>Better code reusability</strong>: Since the strategies are encapsulated and interchangeable, they can be reused across different objects and projects.</li>
<li><strong>Encourages better coding practices</strong>: This pattern promotes good coding practices, such as separating concerns and reducing code complexity.</li>
<li><strong>Simplifies testing</strong>: By separating the algorithms and behaviors from the object, testing becomes more straightforward.</li>
</ol>
<h2 id="heading-use-cases-for-the-strategy-design-pattern">Use Cases for the Strategy Design Pattern</h2>
<p>The Strategy Design Pattern can be useful in various scenarios, such as:</p>
<ol>
<li><strong>Sorting algorithms</strong>: Different sorting algorithms can be encapsulated into separate strategies and passed to an object that needs sorting.</li>
<li><strong>Validation rules</strong>: Different validation rules can be encapsulated into separate strategies and passed to an object that needs validation.</li>
<li><strong>Text formatting</strong>: Different formatting strategies can be encapsulated into separate strategies and passed to an object that needs formatting.</li>
<li><strong>Database access</strong>: Different database access strategies can be encapsulated into separate strategies and passed to an object that needs to access data from different sources.</li>
<li><strong>Payment strategy</strong>: Different payment methods can be encapsulated into separate strategies and passed to an object that needs to process payments.</li>
</ol>
<h2 id="heading-understanding-the-strategy-design-pattern">Understanding the Strategy Design Pattern</h2>
<p>The Strategy Design Pattern is a powerful pattern in the world of object-oriented programming. It provides a flexible way to encapsulate and swap the behavior of an object at runtime, enabling code to be more adaptable and easier to maintain. </p>
<p>In this section, we will dive deeper into the Strategy Design Pattern, discussing its definition, components, and how it works.</p>
<h3 id="heading-components-of-the-strategy-design-pattern">Components of the Strategy Design Pattern</h3>
<p>The Strategy Design Pattern consists of three primary components:</p>
<ol>
<li><strong>Context</strong>: The object that will delegate its behavior to one of the contained strategies. The context maintains a reference to a strategy object and interacts with it through a common interface.</li>
<li><strong>Strategy Interface</strong>: The interface that defines the behavior for all strategies. The strategies implement this interface to provide their unique implementation of the behavior.</li>
<li><strong>Concrete Strategies</strong>: The classes that implement the Strategy Interface. Each strategy encapsulates a specific behavior that the context can switch to at runtime.</li>
</ol>
<h3 id="heading-how-the-strategy-design-pattern-works">How the Strategy Design Pattern Works</h3>
<p>The Strategy Design Pattern works by separating the behavior of an object from the object itself. The behavior is encapsulated into different strategies, each with its own implementation of the behavior. </p>
<p>The context maintains a reference to a strategy object and interacts with it through a common interface. At runtime, the context can swap the current strategy with another one, effectively changing the object's behavior.</p>
<h3 id="heading-examples-of-the-strategy-design-pattern-in-action">Examples of the Strategy Design Pattern in Action</h3>
<p>One example of the Strategy Design Pattern in action is in a music streaming service where different subscription tiers have different pricing models. </p>
<p>Each subscription tier could have a different pricing strategy that encapsulates its unique pricing logic. The service's billing system would delegate the pricing calculation to the current subscription's strategy, allowing for easy modification and extension of the pricing logic.</p>
<p>Another example is payment strategies. Different payment methods can be encapsulated into separate strategies, each with its own unique processing logic. </p>
<p>A shopping cart application may use the Strategy Design Pattern to encapsulate credit card, PayPal, and cryptocurrency payment methods into separate strategies that can be swapped at runtime. The application's payment processing system would delegate the payment processing logic to the current payment method's strategy, allowing for easy modification and extension of the payment processing logic.</p>
<h2 id="heading-how-to-implement-the-strategy-design-pattern">How to Implement the Strategy Design Pattern</h2>
<p>In this section, we will discuss how to implement the Strategy Design Pattern. We will start with a code example that violates the Strategy Design Pattern and explain the problems with it. Then, we will refactor the code to demonstrate how to implement the Strategy Design Pattern.</p>
<p>To implement the Strategy Design Pattern in Java, follow these steps:</p>
<ol>
<li>Identify the algorithm or behavior that needs to be encapsulated and made interchangeable.</li>
<li>Define an interface that represents the behavior, with a single method signature that takes in any required parameters.</li>
<li>Implement concrete classes that provide specific implementations of the behavior defined in the interface.</li>
<li>Define a context class that holds a reference to the interface and calls its method when needed.</li>
<li>Modify the context class to allow for the dynamic swapping of the concrete implementations at runtime.</li>
</ol>
<h3 id="heading-code-example">Code Example</h3>
<p>Let's consider the following code example:</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> withoutstrategy;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentProcessor</span> </span>{
    <span class="hljs-keyword">private</span> PaymentType paymentType;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">processPayment</span><span class="hljs-params">(<span class="hljs-keyword">double</span> amount)</span> </span>{
        <span class="hljs-keyword">if</span> (paymentType == PaymentType.CREDIT_CARD) {
            System.out.println(<span class="hljs-string">"Processing credit card payment of amount "</span> + amount);
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (paymentType == PaymentType.DEBIT_CARD) {
            System.out.println(<span class="hljs-string">"Processing debit card payment of amount "</span> + amount);
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (paymentType == PaymentType.PAYPAL) {
            System.out.println(<span class="hljs-string">"Processing PayPal payment of amount "</span> + amount);
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Invalid payment type"</span>);
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setPaymentType</span><span class="hljs-params">(PaymentType paymentType)</span> </span>{
        <span class="hljs-keyword">this</span>.paymentType = paymentType;
    }
}

<span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">PaymentType</span> </span>{
    CREDIT_CARD,
    DEBIT_CARD,
    PAYPAL
}
</code></pre>
<p>In this code, the <code>PaymentProcessor</code> class has a <code>processPayment</code> method that takes a payment amount and processes the payment. The payment type is set using the <code>setPaymentType</code> method, which sets the <code>paymentType</code> field. The <code>processPayment</code> method then checks the value of <code>paymentType</code> and processes the payment accordingly.</p>
<p>The problem with this code is that it violates the <a target="_blank" href="https://www.freecodecamp.org/news/open-closed-principle-solid-architecture-concept-explained/">Open-Closed Principle</a>, which states that classes should be open for extension but closed for modification. In this code, if you want to add a new payment type, you would have to modify the <code>processPayment</code> method, which violates the Open-Closed Principle.</p>
<p>The <code>PaymentProcessor</code> class violates the Strategy pattern by using conditional statements to determine the type of payment and then processing it accordingly. This approach can quickly become unmanageable and inflexible as the number of payment types increases.</p>
<p>To fix this problem, you can use the Strategy Design Pattern. First, you define a common interface for all payment strategies, which in this case is the <code>PaymentStrategy</code> interface:</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> withstrategy;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">PaymentStrategy</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">processPayment</span><span class="hljs-params">(<span class="hljs-keyword">double</span> amount)</span></span>;
}
</code></pre>
<p>You then define concrete implementations of the <code>PaymentStrategy</code> interface for each payment type. For example, here are the <code>CreditCardPaymentStrategy</code>, <code>DebitCardPaymentStrategy</code>, and <code>PaypalPaymentStrategy</code> classes:</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> withstrategy;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreditCardPaymentStrategy</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">PaymentStrategy</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">processPayment</span><span class="hljs-params">(<span class="hljs-keyword">double</span> amount)</span> </span>{
        System.out.println(<span class="hljs-string">"Processing credit card payment of amount "</span> + amount);
    }
}
</code></pre>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> withstrategy;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DebitCardPaymentStrategy</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">PaymentStrategy</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">processPayment</span><span class="hljs-params">(<span class="hljs-keyword">double</span> amount)</span> </span>{
        System.out.println(<span class="hljs-string">"Processing debit card payment of amount "</span> + amount);
    }
}
</code></pre>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> withstrategy;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaypalPaymentStrategy</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">PaymentStrategy</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">processPayment</span><span class="hljs-params">(<span class="hljs-keyword">double</span> amount)</span> </span>{
        System.out.println(<span class="hljs-string">"Processing PayPal payment of amount "</span> + amount);
    }
}
</code></pre>
<p>Finally, you update the <code>PaymentProcessor</code> class to take a <code>PaymentStrategy</code> object in its constructor, which it uses to process the payment:</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> withstrategy;

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentProcessor</span> </span>{
    <span class="hljs-keyword">private</span> PaymentStrategy paymentStrategy;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">PaymentProcessor</span><span class="hljs-params">(PaymentStrategy paymentStrategy)</span> </span>{
        <span class="hljs-keyword">this</span>.paymentStrategy = paymentStrategy;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">processPayment</span><span class="hljs-params">(<span class="hljs-keyword">double</span> amount)</span> </span>{
        paymentStrategy.processPayment(amount);
    }
}
</code></pre>
<p>This implementation follows the Open-Closed Principle as well as Strategy Pattern because you can add new payment types by creating new implementations of the <code>PaymentStrategy</code> interface without modifying the existing code.</p>
<h3 id="heading-best-practices-for-implementing-the-strategy-design-pattern">Best Practices for Implementing the Strategy Design Pattern</h3>
<p>Here are a few best practices to keep in mind when implementing the Strategy Design Pattern:</p>
<ol>
<li>Keep the interface simple and focused on a single responsibility.</li>
<li>Encapsulate any stateful behavior in the concrete strategy classes, rather than in the context class.</li>
<li>Use dependency injection to pass the concrete strategy to the context class, rather than creating it directly in the context class.</li>
<li>Use an enum or a factory class to provide a centralized place for creating and managing concrete strategy objects.</li>
</ol>
<h2 id="heading-real-world-applications-of-the-strategy-design-pattern">Real-World Applications of the Strategy Design Pattern</h2>
<p>The Strategy Design Pattern has been used extensively in various real-world applications. One such example is the <strong>Java Collections Framework</strong>. The Collections Framework provides a set of interfaces and classes to represent collections of objects, such as lists, sets, and maps. The framework allows different strategies to be applied to collections based on their behavior.</p>
<p>For instance, the Collections Framework includes a <code>sort()</code> method that allows the sorting of collections. The <code>sort()</code> method takes a Comparator object as an argument, which is responsible for comparing objects within the collection. The Comparator interface defines a strategy for comparing two objects, and the <code>sort()</code> method uses this strategy to sort the collection.</p>
<p>In addition, the Collections Framework also includes the Iterator interface, which defines a strategy for accessing elements of a collection. The Iterator allows the user to traverse the collection without exposing its internal structure, which can change over time. By using the Iterator interface, the user can switch between different strategies for accessing elements of the collection.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>In this tutorial, we have explored the Strategy Design Pattern and its implementation in Java. We have seen how the Strategy pattern can be used to separate the behavior of an object from its implementation, providing greater flexibility and maintainability in code.</p>
<p>We discussed the components of the Strategy Design Pattern, including the Context, Strategy Interface, and Concrete Strategies. We also provided an example of how the pattern can be used to implement a payment system, allowing for multiple payment options to be implemented using a single interface.</p>
<p>By separating the behavior of an object from its implementation, the Strategy pattern provides greater flexibility and adaptability to changing requirements.</p>
<h3 id="heading-additional-resources">Additional Resources</h3>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/solid-principles-for-better-software-design/">SOLID Principles for Better Software Design</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/a-beginners-guide-to-the-strategy-design-pattern/freecodecamp.org/news/javascript-design-patterns-explained/">Design Patterns explained</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
