<?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[ Tutorial - 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[ Tutorial - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 18 Jun 2026 23:23:35 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/tutorial/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Browser-Based PDF Crop Tool Using JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ PDF files often contain unwanted margins, blank spaces, scanner borders, page headers, page footers, or unnecessary content around the main document area. Cropping allows users to remove these unwante ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-pdf-crop-tool-javascript/</link>
                <guid isPermaLink="false">6a2cfab7f3a6ae5b0409749b</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Online PDF Tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Programming Blogs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bhavin Sheth ]]>
                </dc:creator>
                <pubDate>Sat, 13 Jun 2026 06:37:43 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/4849599a-a0bc-4cb7-9d14-86bb990d000d.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>PDF files often contain unwanted margins, blank spaces, scanner borders, page headers, page footers, or unnecessary content around the main document area.</p>
<p>Cropping allows users to remove these unwanted areas and focus only on the important content.</p>
<p>In this tutorial, you'll build a browser-based PDF Crop Tool using JavaScript.</p>
<p>Users will be able to upload a PDF, preview pages, select a crop area visually, apply crop settings to specific pages, generate a cropped PDF, preview the final result, and download the updated document directly from the browser.</p>
<p>Everything runs locally without requiring a backend server.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-why-pdf-cropping-is-useful">Why PDF Cropping Is Useful</a></p>
</li>
<li><p><a href="#heading-how-pdf-cropping-works">How PDF Cropping Works</a></p>
</li>
<li><p><a href="#heading-project-setup">Project Setup</a></p>
</li>
<li><p><a href="#heading-what-library-are-we-using">What Library Are We Using?</a></p>
</li>
<li><p><a href="#heading-creating-the-upload-interface">Creating the Upload Interface</a></p>
</li>
<li><p><a href="#heading-previewing-uploaded-pdf-pages">Previewing Uploaded PDF Pages</a></p>
</li>
<li><p><a href="#heading-configuring-crop-settings">Configuring Crop Settings</a></p>
</li>
<li><p><a href="#heading-applying-the-crop">Applying the Crop</a></p>
</li>
<li><p><a href="#heading-generating-the-cropped-pdf">Generating the Cropped PDF</a></p>
</li>
<li><p><a href="#heading-why-pdf-cropping-is-useful-in-real-world-documents">Why PDF Cropping Is Useful in Real-World Documents</a></p>
</li>
<li><p><a href="#heading-demo-cropping-pdf-files-in-the-browser">Demo: Cropping PDF Files in the Browser</a></p>
</li>
<li><p><a href="#heading-important-notes-from-real-world-use">Important Notes from Real-World Use</a></p>
</li>
<li><p><a href="#heading-common-mistakes-to-avoid">Common Mistakes to Avoid</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-why-pdf-cropping-is-useful">Why PDF Cropping Is Useful</h2>
<p>PDF cropping is commonly used when working with scanned documents, invoices, reports, contracts, forms, ebooks, manuals, presentations, and academic documents.</p>
<p>Many PDFs contain unnecessary whitespace or scanning artifacts around the edges of the page. Cropping helps remove distractions and makes documents easier to read.</p>
<p>Businesses often crop invoices and reports before sharing them with clients. Students crop lecture notes and scanned study materials to focus on the important content. And designers frequently crop exported PDFs to remove unwanted margins before printing or publishing.</p>
<p>Cropping also reduces visual clutter and creates a cleaner, more professional document.</p>
<h2 id="heading-how-pdf-cropping-works">How PDF Cropping Works</h2>
<p>A PDF crop tool loads document pages inside the browser and allows users to define a rectangular crop area.</p>
<p>Once selected, the crop coordinates are applied to the chosen pages. The browser then generates a new PDF using only the selected content area.</p>
<p>Everything happens locally inside the browser. This means uploaded documents never leave the user's device, improving privacy and security.</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>This project is intentionally simple: you'll only need an HTML file, a JavaScript file, and a PDF processing library.</p>
<p>No backend server or database is required, as everything runs directly inside the browser.</p>
<h2 id="heading-what-library-are-we-using">What Library Are We Using?</h2>
<p>We'll use PDF-lib for PDF processing.</p>
<p>PDF-lib allows us to load PDF documents, modify page boundaries, and export updated PDF files directly in JavaScript.</p>
<p>Add the library using a CDN:</p>
<pre><code class="language-html">&lt;script src="https://unpkg.com/pdf-lib/dist/pdf-lib.min.js"&gt;&lt;/script&gt;
</code></pre>
<p>Once loaded, JavaScript can process PDF pages directly inside the browser.</p>
<h2 id="heading-creating-the-upload-interface">Creating the Upload Interface</h2>
<p>Users first upload a PDF document into the browser.</p>
<p>A simple file input works well:</p>
<pre><code class="language-html">&lt;input type="file" id="pdfInput" accept=".pdf"&gt;
</code></pre>
<p>JavaScript can detect when a file is selected:</p>
<pre><code class="language-javascript">document.getElementById("pdfInput").addEventListener("change", (event) =&gt; {
  const file = event.target.files[0];
  console.log(file.name);
});
</code></pre>
<p>Here's what the upload section looks like:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/5e4c19dd-0946-4d94-959e-ca537a5d31d4.png" alt="PDF upload interface for browser-based PDF crop tool" style="display:block;margin:0 auto" width="824" height="665" loading="lazy">

<h2 id="heading-previewing-uploaded-pdf-pages">Previewing Uploaded PDF Pages</h2>
<p>After uploading a document, users can preview PDF pages directly inside the browser.</p>
<p>The preview area includes page navigation controls that allow users to move between pages before cropping.</p>
<p>A default crop selection area is displayed on the preview page to help users begin selecting content immediately. This makes it easier to verify the document before applying crop settings.</p>
<p>Here's what the preview section looks like:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/0b1255e5-5616-4633-87fb-b4e8edcf3901.png" alt="PDF page preview with page navigation and crop selection area" style="display:block;margin:0 auto" width="764" height="757" loading="lazy">

<h2 id="heading-configuring-crop-settings">Configuring Crop Settings</h2>
<p>After users select a crop area on the PDF preview, they often need more precise control over how the crop should be applied.</p>
<p>A practical PDF crop tool should allow users to manually adjust crop coordinates, choose predefined page ratios, and decide which pages should receive the crop operation.</p>
<p>This flexibility is especially useful when working with scanned documents, forms, reports, ebooks, presentations, and multi-page PDFs where different pages may require different crop settings.</p>
<p>In this project, users can adjust the crop position, control the crop dimensions, choose predefined crop ratios, and decide whether the crop should be applied to the current page, all pages, or a specific page range.</p>
<p>The crop settings panel provides complete control before generating the final PDF.</p>
<p>Here's what the crop settings section looks like:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/e4754c1f-d82e-4404-853a-a585a9cdfc14.png" alt="PDF crop settings with crop coordinates predefined ratios and page selection options" style="display:block;margin:0 auto" width="668" height="742" loading="lazy">

<h3 id="heading-reading-crop-coordinates">Reading Crop Coordinates</h3>
<p>When users drag a selection area on the preview page, the application records the crop dimensions.</p>
<p>A crop object typically contains:</p>
<pre><code class="language-javascript">const cropArea = {
  x: 173,
  y: 141,
  width: 452,
  height: 309
};
</code></pre>
<p>These values determine which portion of the page will remain visible after cropping.</p>
<h3 id="heading-applying-custom-coordinates">Applying Custom Coordinates</h3>
<p>Users can manually modify crop coordinates for more accurate results.</p>
<p>For example:</p>
<pre><code class="language-javascript">const left = parseInt(document.getElementById("cropX").value);
const top = parseInt(document.getElementById("cropY").value);
const width = parseInt(document.getElementById("cropWidth").value);
const height = parseInt(document.getElementById("cropHeight").value);
</code></pre>
<p>These values are later used when applying the crop to the PDF page.</p>
<h3 id="heading-supporting-predefined-crop-ratios">Supporting Predefined Crop Ratios</h3>
<p>Many users don't want to manually enter crop coordinates every time they crop a document.</p>
<p>In real-world situations, documents often need to follow standard page dimensions. Instead of adjusting the crop area manually, users can quickly choose a predefined ratio and let the tool apply the appropriate crop settings automatically.</p>
<p>For example, a user preparing documents for printing may choose an A4 layout, while someone working with presentation slides may prefer a landscape format. Other users may simply want to remove margins while keeping the original page proportions intact.</p>
<p>A simple example looks like this:</p>
<pre><code class="language-javascript">function applyA4Portrait() {
  cropArea = {
    x: 0,
    y: 0,
    width: 595,
    height: 842
  };
}
</code></pre>
<p>This allows users to instantly apply a standard page size.</p>
<h3 id="heading-selecting-pages-to-crop">Selecting Pages to Crop</h3>
<p>Not every page requires cropping. Some users may only want to crop a single page while leaving the rest of the document unchanged.</p>
<p>The tool supports three page selection modes:</p>
<p>Current page only:</p>
<pre><code class="language-javascript">const applyMode = "current";
</code></pre>
<p>All pages:</p>
<pre><code class="language-javascript">const applyMode = "all";
</code></pre>
<p>Specific page ranges:</p>
<pre><code class="language-javascript">const applyMode = "specific";
const pageRange = "1,3-5,10";
</code></pre>
<p>This gives users full control over where the crop should be applied.</p>
<h3 id="heading-applying-crop-settings-to-pdf-pages">Applying Crop Settings to PDF Pages</h3>
<p>Once the crop values are finalized, the selected pages can be updated using PDF-lib.</p>
<p>A simplified example looks like this:</p>
<pre><code class="language-javascript">const pages = pdfDoc.getPages();

pages.forEach((page) =&gt; {
  page.setCropBox(
    cropArea.x,
    cropArea.y,
    cropArea.width,
    cropArea.height
  );
});
</code></pre>
<p>The crop box defines the visible area that will remain in the generated PDF.</p>
<h3 id="heading-validating-crop-values">Validating Crop Values</h3>
<p>Before applying the crop, it's important to verify that users entered valid dimensions.</p>
<p>For example:</p>
<pre><code class="language-javascript">if (
  cropArea.width &lt;= 0 ||
  cropArea.height &lt;= 0
) {
  alert("Invalid crop size");
  return;
}
</code></pre>
<p>Validation helps prevent errors and ensures the final PDF is generated correctly.</p>
<p>After the crop settings are configured, users can proceed to generate the updated PDF and review the results before downloading the final document.</p>
<h2 id="heading-applying-the-crop">Applying the Crop</h2>
<p>Once the crop settings are configured, users can apply the crop operation.</p>
<p>For example:</p>
<pre><code class="language-javascript">page.setCropBox(x, y, width, height);
</code></pre>
<p>The selected crop area is applied to the chosen pages before generating the updated document.</p>
<p>Here's what the crop action section looks like:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/0bcfb644-71b0-4f84-a08b-c0aed34ce420.png" alt="Crop PDF button and start over option" style="display:block;margin:0 auto" width="175" height="80" loading="lazy">

<h2 id="heading-generating-the-cropped-pdf">Generating the Cropped PDF</h2>
<p>After cropping is complete, the browser generates a new PDF document containing only the selected page areas.</p>
<p>For example:</p>
<pre><code class="language-javascript">const pdfBytes = await pdfDoc.save();
</code></pre>
<p>The updated file can then be previewed and downloaded.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/ac18fcf4-d632-4b32-9767-70e69667c98a.png" alt="GENERATED PDF CROP FILE SHOWING WITH ITS PREVIEW" style="display:block;margin:0 auto" width="1200" height="645" loading="lazy">

<h2 id="heading-why-pdf-cropping-is-useful-in-real-world-documents">Why PDF Cropping Is Useful in Real-World Documents</h2>
<p>PDF cropping is one of those features that seems simple at first, but it becomes incredibly useful once you start working with real-world documents.</p>
<p>Many PDFs contain content that users don't actually need. Scanned documents often include large white borders created by scanners. Screenshots converted into PDFs may contain unnecessary background areas. Reports and presentations frequently have oversized margins that waste space and make documents harder to read.</p>
<p>By cropping unwanted areas, users can focus attention on the content that actually matters. The final document becomes cleaner, easier to read, more professional, and often more suitable for printing.</p>
<p>PDF cropping is especially valuable in business environments where documents are processed in large quantities.</p>
<p>For example, many e-commerce sellers on platforms such as Flipkart, Amazon, Meesho, and other marketplaces regularly download shipping labels, invoices, and packing slips in PDF format.</p>
<p>Imagine receiving a PDF containing 100 shipping labels for customer orders. The downloaded file may include unnecessary margins, extra whitespace, instructions, or content outside the area that needs to be printed.</p>
<p>Instead of manually editing every page, users can define a crop area once and apply the same crop settings to all pages in the document. This automatically removes unwanted content from all 100 labels in a single operation.</p>
<p>The result is a cleaner PDF that contains only the information required for printing and packaging.</p>
<p>The same workflow is useful for:</p>
<ul>
<li><p>E-commerce packing slips</p>
</li>
<li><p>Warehouse barcode sheets</p>
</li>
<li><p>Courier documentation</p>
</li>
<li><p>Invoices and billing documents</p>
</li>
<li><p>Scanned contracts and agreements</p>
</li>
<li><p>Academic research papers</p>
</li>
<li><p>Government forms</p>
</li>
<li><p>Business reports and presentations</p>
</li>
<li><p>Construction drawings and engineering documents</p>
</li>
<li><p>Training manuals and internal company documentation</p>
</li>
</ul>
<p>Cropping can also significantly improve printing efficiency. When unnecessary margins are removed, the important content occupies more of the printable area, making labels, invoices, diagrams, and reports easier to read.</p>
<p>Another common use case involves scanned paperwork. Many scanner applications automatically capture extra background around a document. Cropping removes these unwanted edges and produces a cleaner digital copy without requiring image-editing software.</p>
<p>Because the crop area can be applied to the current page, all pages, or specific page ranges, users can process large PDF documents in seconds rather than manually editing pages one by one.</p>
<p>For businesses that handle hundreds of PDF files every week, this can save a significant amount of time while producing cleaner, more professional documents ready for sharing, printing, or archiving.</p>
<h2 id="heading-demo-how-the-pdf-crop-tool-works">Demo: How the PDF Crop Tool Works</h2>
<h3 id="heading-step-1-upload-a-pdf-file">Step 1: Upload a PDF File</h3>
<p>Users begin by uploading a PDF document into the browser.</p>
<p>The upload area supports drag-and-drop functionality and manual file selection.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/2d5c10b2-d151-4194-a573-7137d3097e2e.png" alt="Upload PDF file for cropping" style="display:block;margin:0 auto" width="824" height="665" loading="lazy">

<h3 id="heading-step-2-preview-the-uploaded-pdf">Step 2: Preview the Uploaded PDF</h3>
<p>After uploading the document, the browser displays a page preview.</p>
<p>Users can move between pages using the navigation controls.</p>
<p>A default crop selection area is displayed to simplify the cropping process.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/06b0efe9-161b-484f-bd8f-034deef47d79.png" alt="Uploaded PDF preview with crop selection and page navigation" style="display:block;margin:0 auto" width="764" height="757" loading="lazy">

<h3 id="heading-step-3-configure-crop-settings">Step 3: Configure Crop Settings</h3>
<p>Users can fine-tune crop coordinates, choose predefined ratios, and select which pages should receive the crop.</p>
<p>The crop can be applied to a single page, all pages, or specific page ranges.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/f5ed20db-b7de-4358-940f-b3d3ea2e7ac6.png" alt="Configure crop coordinates ratios and page settings" style="display:block;margin:0 auto" width="668" height="742" loading="lazy">

<h3 id="heading-step-4-apply-the-crop">Step 4: Apply the Crop</h3>
<p>Once everything is configured, users click the Crop PDF button.</p>
<p>The browser processes the selected pages and applies the crop settings.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/d0f1d324-1445-47bf-9809-b2a8653dbf62.png" alt="Apply crop operation to PDF pages" style="display:block;margin:0 auto" width="175" height="80" loading="lazy">

<h3 id="heading-step-5-preview-the-cropped-pdf">Step 5: Preview the Cropped PDF</h3>
<p>After processing is complete, users can preview the cropped document.</p>
<p>Page navigation controls allow users to review every cropped page before downloading.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/feee3a65-9896-4e27-8172-b0ff8bae5216.png" alt="Preview cropped PDF with page navigation controls" style="display:block;margin:0 auto" width="662" height="519" loading="lazy">

<h3 id="heading-step-6-download-the-cropped-pdf">Step 6: Download the Cropped PDF</h3>
<p>The final section displays the generated file.</p>
<p>Users can rename the document, review file details such as total pages and file size, and download the cropped PDF.</p>
<p>A Start Over button is also available for processing another file.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/f0005bca-0e60-4ef7-862f-28f61f38fea8.png" alt="Download cropped PDF with filename page count and file size details" style="display:block;margin:0 auto" width="661" height="383" loading="lazy">

<h2 id="heading-important-notes-from-real-world-use">Important Notes from Real-World Use</h2>
<p>When working with large PDF files, processing may take longer depending on the number of pages.</p>
<p>Always validate uploaded files before loading them.</p>
<p>For example:</p>
<pre><code class="language-javascript">if (!file.name.endsWith(".pdf")) {
  alert("Please upload a PDF file");
  return;
}
</code></pre>
<p>Previewing pages before downloading helps catch cropping mistakes early.</p>
<h2 id="heading-common-mistakes-to-avoid">Common Mistakes to Avoid</h2>
<p>One common mistake is selecting crop coordinates that remove important document content.</p>
<p>Another mistake is applying a crop to all pages when only specific pages should be modified.</p>
<p>For example:</p>
<pre><code class="language-javascript">if (!cropArea) {
  alert("Select a crop area first");
  return;
}
</code></pre>
<p>Always review the cropped preview before downloading the final document.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you built a browser-based PDF Crop Tool using JavaScript.</p>
<p>You learned how to upload PDF files, preview pages, define crop areas, configure crop settings, apply cropping operations, generate updated PDFs, and download the final document directly from the browser.</p>
<p>More importantly, you saw how modern browsers can handle PDF editing tasks locally without requiring a backend server. This approach keeps document processing fast, private, and easy to use.</p>
<p>If you'd like to see a working example, try the <a href="https://allinonetools.net/crop-pdf/"><strong>AllinoneTools-</strong> <strong>PDF Crop Tool</strong></a> and explore how PDF pages can be cropped directly in the browser.</p>
<p>Once you understand this workflow, you can extend it further with features like PDF rotation, page organization, watermarking, metadata editing, annotations, and advanced PDF editing tools.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Browser-Based PDF to Image Converter Using JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Whether it’s invoices, scanned documents, reports, certificates, or receipts, users often need to convert PDF pages into image files quickly. Modern browsers make this much easier than before. Instead ]]>
                </description>
                <link>https://www.freecodecamp.org/news/pdf-to-image-converter/</link>
                <guid isPermaLink="false">6a024b87fca21b0d4b6cbcd9</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ pdf ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webdev ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bhavin Sheth ]]>
                </dc:creator>
                <pubDate>Mon, 11 May 2026 21:35:03 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/d412f56a-2860-4b61-a300-ab3511c34e78.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Whether it’s invoices, scanned documents, reports, certificates, or receipts, users often need to convert PDF pages into image files quickly.</p>
<p>Modern browsers make this much easier than before.</p>
<p>Instead of uploading documents to a server, we can process PDF files directly inside the browser using JavaScript. This keeps the tool fast, private, and easy to use.</p>
<p>In this tutorial, you’ll build a browser-based PDF to image converter using JavaScript.</p>
<p>The tool will support uploading PDF files, previewing pages, selecting image formats like JPG or PNG, adjusting image quality, and downloading converted images directly from the browser.</p>
<p>Everything runs entirely client-side without any backend.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a href="#heading-how-pdf-to-image-conversion-works">How PDF to Image Conversion Works</a></p>
</li>
<li><p><a href="#heading-project-setup">Project Setup</a></p>
</li>
<li><p><a href="#heading-what-library-are-we-using">What Library Are We Using?</a></p>
</li>
<li><p><a href="#heading-creating-the-upload-interface">Creating the Upload Interface</a></p>
</li>
<li><p><a href="#heading-reading-the-pdf-file">Reading the PDF File</a></p>
</li>
<li><p><a href="#heading-rendering-pdf-pages-as-images">Rendering PDF Pages as Images</a></p>
</li>
<li><p><a href="#heading-selecting-image-format-and-quality">Selecting Image Format and Quality</a></p>
</li>
<li><p><a href="#heading-generating-and-downloading-images">Generating and Downloading Images</a></p>
</li>
<li><p><a href="#heading-demo-how-the-pdf-to-image-tool-works">Demo: How the PDF to Image Tool Works</a></p>
</li>
<li><p><a href="#heading-important-notes-from-real-world-use">Important Notes from Real-World Use</a></p>
</li>
<li><p><a href="#heading-common-mistakes-to-avoid">Common Mistakes to Avoid</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-how-pdf-to-image-conversion-works">How PDF to Image Conversion Works</h2>
<p>A browser can't directly convert PDF files into images on its own.</p>
<p>Instead, JavaScript libraries render PDF pages onto an HTML canvas, which can then be exported as image files like JPG or PNG.</p>
<p>The process starts when users upload a PDF document into the browser. JavaScript then reads the file, renders each PDF page visually onto a canvas, converts those rendered pages into image files, and finally makes them available for download.</p>
<p>Everything happens locally inside the browser.</p>
<p>This means users don't need to upload private documents to external servers, making the process faster and more privacy-friendly.</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>This project is intentionally simple. Everything runs directly inside the browser using JavaScript, so no backend or server setup is required.</p>
<p>You only need:</p>
<ul>
<li><p>an HTML file</p>
</li>
<li><p>a JavaScript file</p>
</li>
<li><p>the PDF.js library</p>
</li>
</ul>
<h2 id="heading-what-library-are-we-using">What Library Are We Using?</h2>
<p>We’ll use Mozilla’s PDF.js library to render PDF pages inside the browser.</p>
<p>Add it using a CDN:</p>
<pre><code class="language-html">&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"&gt;&lt;/script&gt;
</code></pre>
<p>Once loaded, the browser can read and render PDF pages directly using JavaScript.</p>
<h2 id="heading-creating-the-upload-interface">Creating the Upload Interface</h2>
<p>Start with a simple upload area:</p>
<pre><code class="language-html">&lt;input type="file" id="pdfUpload" accept="application/pdf"&gt;

&lt;select id="format"&gt;
  &lt;option&gt;JPG&lt;/option&gt;
  &lt;option&gt;PNG&lt;/option&gt;
  &lt;option&gt;WEBP&lt;/option&gt;
&lt;/select&gt;

&lt;input type="range" id="quality" min="10" max="100" value="90"&gt;

&lt;button onclick="convertPDF()"&gt;
  Convert to Images
&lt;/button&gt;
</code></pre>
<p>This allows users to upload PDF files directly into the browser.</p>
<p>Here’s what the upload section looks like inside the tool:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/09e2683b-617c-4703-9e6b-78c7b25c6000.png" alt="PDF upload interface inside browser-based PDF to image converter" style="display:block;margin:0 auto" width="1398" height="681" loading="lazy">

<h2 id="heading-reading-the-pdf-file">Reading the PDF File</h2>
<p>After the file is uploaded, we need to read it using JavaScript.</p>
<p>For example:</p>
<pre><code class="language-javascript">const file = document.getElementById("pdfUpload").files[0];

const reader = new FileReader();

reader.onload = async function () {
  const typedArray = new Uint8Array(reader.result);

  const pdf = await pdfjsLib.getDocument(typedArray).promise;

  console.log(pdf.numPages);
};

reader.readAsArrayBuffer(file);
</code></pre>
<p>This loads the PDF document directly inside the browser.</p>
<p>You can then access each page individually.</p>
<h2 id="heading-rendering-pdf-pages-as-images">Rendering PDF Pages as Images</h2>
<p>Once the PDF is loaded, pages can be rendered onto a canvas.</p>
<p>For example:</p>
<pre><code class="language-javascript">const page = await pdf.getPage(1);

const viewport = page.getViewport({ scale: 2 });

const canvas = document.createElement("canvas");

const context = canvas.getContext("2d");

canvas.width = viewport.width;
canvas.height = viewport.height;

await page.render({
  canvasContext: context,
  viewport: viewport
}).promise;
</code></pre>
<p>This renders the selected PDF page visually inside the browser.</p>
<p>After rendering, the canvas can be converted into an image.</p>
<p>For example:</p>
<pre><code class="language-javascript">const imageData = canvas.toDataURL("image/jpeg", 0.9);
</code></pre>
<p>This creates a downloadable image version of the PDF page.</p>
<h2 id="heading-selecting-image-format-and-quality">Selecting Image Format and Quality</h2>
<p>Before generating the final images, users may want to customize output settings.</p>
<p>Different image formats work better for different situations.</p>
<p>For example:</p>
<ul>
<li><p>JPG works well for smaller file sizes</p>
</li>
<li><p>PNG preserves better quality</p>
</li>
<li><p>WEBP offers modern compression</p>
</li>
</ul>
<p>Users can also control image quality using a slider.</p>
<p>For example:</p>
<pre><code class="language-javascript">canvas.toDataURL("image/jpeg", 0.8);
</code></pre>
<p>The value <code>0.8</code> controls compression quality.</p>
<p>Here’s an example of image format and quality settings inside the tool:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/e315c633-3cac-434b-9564-294bce940e99.png" alt=" Image format selection options and quality slider inside PDF to image converter" style="display:block;margin:0 auto" width="843" height="238" loading="lazy">

<h2 id="heading-generating-and-downloading-images">Generating and Downloading Images</h2>
<p>Once pages are rendered, images can be downloaded directly from the browser.</p>
<p>For example:</p>
<pre><code class="language-javascript">const link = document.createElement("a");

link.href = imageData;

link.download = `page-${pageNumber}.jpg`;

link.click();
</code></pre>
<p>This downloads the generated image instantly.</p>
<p>When working with multi-page PDFs, the same process can run for every page automatically.</p>
<p>This allows users to export complete PDF documents as separate image files.</p>
<h2 id="heading-demo-how-the-pdf-to-image-tool-works">Demo: How the PDF to Image Tool Works</h2>
<p>For this example, we’ll convert PDF pages into downloadable image files directly inside the browser.</p>
<h3 id="heading-step-1-upload-pdf-files">Step 1: Upload PDF Files</h3>
<p>Users upload one or more PDF files into the converter.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/e4722a3f-b390-46e4-bb71-a8e4a3ec7138.png" alt="Uploading PDF files into the PDF to image converter" style="display:block;margin:0 auto" width="1398" height="681" loading="lazy">

<h3 id="heading-step-2-preview-uploaded-pages">Step 2: Preview Uploaded Pages</h3>
<p>The tool generates page previews before conversion.</p>
<p>This helps users verify the uploaded document visually.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/3fcd6377-c5ac-4643-a363-7e6c9d4237c0.png" alt="Preview cards showing uploaded PDF pages before conversion" style="display:block;margin:0 auto" width="1310" height="444" loading="lazy">

<h3 id="heading-step-3-configure-output-settings">Step 3: Configure Output Settings</h3>
<p>Users can choose image format and quality settings before generating images.</p>
<p>This allows better control over output size and image clarity.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/e315c633-3cac-434b-9564-294bce940e99.png" alt="Configuring image format and quality settings before conversion" style="display:block;margin:0 auto" width="843" height="238" loading="lazy">

<h3 id="heading-step-4-convert-pdf-pages-into-images">Step 4: Convert PDF Pages into Images</h3>
<p>Once settings are configured, users click the convert button.</p>
<p>The browser processes the PDF locally and generates image files instantly.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/f5b7aaeb-3dfe-4aa3-808f-5a223dd850a1.png" alt="f5b7aaeb-3dfe-4aa3-808f-5a223dd850a1" style="display:block;margin:0 auto" width="358" height="112" loading="lazy">

<h3 id="heading-step-5-download-generated-images">Step 5: Download Generated Images</h3>
<p>After conversion, every PDF page becomes a downloadable image.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/255709e8-3c1d-4d93-9661-5774be70da5b.png" alt="Converted PDF pages exported as downloadable image files" style="display:block;margin:0 auto" width="1188" height="695" loading="lazy">

<h2 id="heading-important-notes-from-real-world-use">Important Notes from Real-World Use</h2>
<p>When working with large PDFs, performance and memory usage become important.</p>
<p>Documents with many pages can slow down rendering if everything is processed at once.</p>
<p>One practical optimization is processing pages step-by-step instead of rendering the entire document immediately.</p>
<p>For example:</p>
<pre><code class="language-javascript">for (let i = 1; i &lt;= pdf.numPages; i++) {
  const page = await pdf.getPage(i);

  // render page
}
</code></pre>
<p>This keeps browser memory usage more stable.</p>
<p>Another useful optimization is reducing render scale for large documents.</p>
<p>For example:</p>
<pre><code class="language-javascript">const viewport = page.getViewport({
  scale: 1.5
});
</code></pre>
<p>Lower scale values generate smaller image files and improve performance.</p>
<p>You can also resize generated images before export.</p>
<p>For example:</p>
<pre><code class="language-javascript">canvas.width = viewport.width;
canvas.height = viewport.height;
</code></pre>
<p>This helps reduce unnecessary file size growth.</p>
<p>Since everything runs locally inside the browser, uploaded PDF files never leave the user’s device, which improves privacy and security.</p>
<h2 id="heading-common-mistakes-to-avoid">Common Mistakes to Avoid</h2>
<p>One common mistake is not validating uploaded files before processing them.</p>
<p>For example:</p>
<pre><code class="language-javascript">if (!file || file.type !== "application/pdf") {
  alert("Please upload a valid PDF file.");
  return;
}
</code></pre>
<p>This prevents unsupported files from breaking the tool.</p>
<p>Another issue is rendering extremely large pages at very high scale values.</p>
<p>Large canvas rendering can consume a lot of memory and slow down conversion significantly.</p>
<p>Using smaller scale values usually improves performance.</p>
<p>Another common mistake is forgetting to wait for page rendering before exporting the image.</p>
<p>For example:</p>
<pre><code class="language-javascript">await page.render({
  canvasContext: context,
  viewport: viewport
}).promise;
</code></pre>
<p>Without <code>await</code>, the image may export before rendering finishes.</p>
<p>Incorrect file naming can also confuse users when multiple pages are generated.</p>
<p>Adding page numbers to filenames improves organization:</p>
<pre><code class="language-javascript">link.download = `page-${pageNumber}.jpg`;
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you built a browser-based PDF to image converter using JavaScript.</p>
<p>You learned how to upload PDF files, render pages inside the browser, generate images, and download them directly without using a backend server.</p>
<p>More importantly, you saw how modern browsers can handle document processing tasks locally while keeping user files private.</p>
<p>This approach keeps the tool fast, lightweight, and easy to use.</p>
<p>Once you understand this workflow, you can extend it further with features like ZIP downloads, batch exports, page selection, watermarking, or image compression.</p>
<p>You can also try a real working version here:</p>
<p><a href="https://allinonetools.net/pdf-to-image-converter/">https://allinonetools.net/pdf-to-image-converter/</a></p>
<p>And that’s where things start getting really interesting.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Barcode Generator Using JavaScript (Step-by-Step) ]]>
                </title>
                <description>
                    <![CDATA[ If you’ve ever worked on something like an inventory system, billing dashboard, or even a small internal tool, chances are you’ve needed to generate barcodes at some point. Most developers either rely ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-barcode-generator/</link>
                <guid isPermaLink="false">69cfdf9b21e7d63506a6957e</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webdev ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Frontend Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bhavin Sheth ]]>
                </dc:creator>
                <pubDate>Fri, 03 Apr 2026 15:41:15 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/684644dd-4128-415f-94ec-cf45b2a80cad.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you’ve ever worked on something like an inventory system, billing dashboard, or even a small internal tool, chances are you’ve needed to generate barcodes at some point.</p>
<p>Most developers either rely on external tools or assume this requires backend processing. That’s usually where things get slower, more complex, and harder to maintain.</p>
<p>But modern browsers have quietly become powerful enough to handle this entirely on their own.</p>
<p>In this tutorial, you’ll build a barcode generator that runs completely in the browser. It won’t upload data anywhere, and it won’t require any server logic. Everything happens instantly on the client side.</p>
<p>Along the way, you’ll also learn how barcode formats work, how to validate inputs properly, and how to create a real-time preview experience that feels responsive and practical.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a href="#heading-how-barcode-generation-works">How Barcode Generation Works</a></p>
</li>
<li><p><a href="#heading-project-setup">Project Setup</a></p>
</li>
<li><p><a href="#heading-what-library-are-we-using">What Library Are We Using?</a></p>
</li>
<li><p><a href="#heading-creating-the-html-structure">Creating the HTML Structure</a></p>
</li>
<li><p><a href="#heading-adding-javascript-for-barcode-generation">Adding JavaScript for Barcode Generation</a></p>
</li>
<li><p><a href="#heading-how-the-barcode-is-generated">How the Barcode Is Generated</a></p>
</li>
<li><p><a href="#heading-types-of-barcodes-you-can-generate">Types of Barcodes You Can Generate</a></p>
</li>
<li><p><a href="#heading-adding-real-time-preview">Adding Real-Time Preview</a></p>
</li>
<li><p><a href="#heading-how-to-validate-input-properly">How to Validate Input Properly</a></p>
</li>
<li><p><a href="#heading-how-to-download-the-barcode">How to Download the Barcode</a></p>
</li>
<li><p><a href="#heading-important-notes-from-real-world-use">Important Notes from Real-World Use</a></p>
</li>
<li><p><a href="#heading-common-mistakes-to-avoid">Common Mistakes to Avoid</a></p>
</li>
<li><p><a href="#heading-demo-how-the-barcode-generator-works">Demo: How the Barcode Generator Works</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-how-barcode-generation-works">How Barcode Generation Works</h2>
<p>A barcode is simply a visual encoding of data. Instead of displaying text directly, it represents that data using a pattern of lines and spaces.</p>
<p>Different barcode formats use different encoding rules. Some support only numbers, while others allow full text input. When you generate a barcode in the browser, you’re essentially converting user input into a structured visual pattern.</p>
<p>The key idea here is that we don’t draw these lines manually. A library takes care of encoding the data and rendering it as an SVG element, which the browser can display instantly.</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>We’ll keep this project intentionally simple so the focus stays on understanding how it works.</p>
<p>All you need is a basic HTML file, a small JavaScript file, and a barcode library. There’s no backend involved, and nothing gets stored or uploaded.</p>
<p>This makes the tool fast, private, and easy to integrate into other projects.</p>
<h2 id="heading-what-library-are-we-using">What Library Are We Using?</h2>
<p>In this project, we use the <strong>JsBarcode</strong> library.</p>
<p>It’s a lightweight JavaScript library that can generate barcodes directly inside the browser using SVG. It supports multiple formats and works without any external dependencies.</p>
<p>You can include it using a CDN:</p>
<pre><code class="language-html">&lt;script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/JsBarcode.all.min.js"&gt;&lt;/script&gt;
</code></pre>
<h2 id="heading-creating-the-html-structure">Creating the HTML Structure</h2>
<p>The interface is simple but practical. It includes an input field where users can enter data, a dropdown to choose the barcode format, and a preview area where the barcode is rendered.</p>
<pre><code class="language-html">&lt;input type="text" id="text" placeholder="Enter text or number"&gt;

&lt;select id="format"&gt;
  &lt;option value="CODE128"&gt;Code128&lt;/option&gt;
  &lt;option value="EAN13"&gt;EAN13&lt;/option&gt;
&lt;/select&gt;

&lt;button onclick="generateBarcode()"&gt;Generate&lt;/button&gt;

&lt;svg id="barcode"&gt;&lt;/svg&gt;
</code></pre>
<p>This structure is enough to handle input, display output, and connect everything through JavaScript.</p>
<h2 id="heading-adding-javascript-for-barcode-generation">Adding JavaScript for Barcode Generation</h2>
<p>Now we'll connect the user input to barcode generation.</p>
<pre><code class="language-javascript">function generateBarcode() {
  const text = document.getElementById("text").value;
  const format = document.getElementById("format").value;

  if (!text) {
    alert("Please enter a value");
    return;
  }

  JsBarcode("#barcode", text, {
    format: format,
    width: 2,
    height: 100,
    displayValue: true
  });
}
</code></pre>
<p>This function reads the input, checks if it exists, and then generates the barcode using the selected format.</p>
<h2 id="heading-how-the-barcode-is-generated">How the Barcode Is Generated</h2>
<p>When you call the JsBarcode function, the library handles everything behind the scenes.</p>
<p>It encodes the input into a barcode standard, converts that into a pattern of lines, and renders it as an SVG element. Because SVG is vector-based, the barcode remains sharp even when resized.</p>
<p>All of this happens instantly in the browser, which is why the experience feels fast.</p>
<h2 id="heading-types-of-barcodes-you-can-generate">Types of Barcodes You Can Generate</h2>
<p>Different barcode formats are used in different industries, and understanding them helps you build more practical tools.</p>
<ol>
<li><p><strong>Code128</strong> is the most flexible format. It supports letters, numbers, and special characters, which makes it ideal for general-purpose use.</p>
</li>
<li><p><strong>EAN-13</strong> is commonly used in retail products. It works only with 13-digit numbers, so it requires strict validation.</p>
</li>
<li><p><strong>UPC</strong> is similar to EAN and is widely used in billing systems, especially in the US. It also expects numeric input with a fixed length.</p>
</li>
<li><p><strong>Code39</strong> is simpler and supports uppercase letters and numbers, but it’s less compact compared to Code128.</p>
</li>
<li><p><strong>ITF-14</strong> is mostly used in logistics and packaging. It’s designed for numeric data and is common in shipping environments.</p>
</li>
</ol>
<p>In most cases, starting with Code128 is the safest option unless you have a specific requirement.</p>
<h2 id="heading-adding-real-time-preview">Adding Real-Time Preview</h2>
<p>One of the biggest improvements you can make to a tool like this is real-time feedback.</p>
<p>Instead of requiring users to click a button every time, you can generate the barcode as they type.</p>
<pre><code class="language-javascript">document.getElementById("text").addEventListener("input", generateBarcode);
document.getElementById("format").addEventListener("change", generateBarcode);
</code></pre>
<p>This small change makes the tool feel much more responsive.</p>
<p>As soon as the user types or changes the format, the barcode updates automatically. This is the same kind of interaction you see in polished production tools.</p>
<h2 id="heading-how-to-validate-input-properly">How to Validate Input Properly</h2>
<p>Validation is where many simple tools break.</p>
<p>Since different barcode formats have different rules, if you don’t validate input correctly, the barcode may fail silently or produce incorrect output.</p>
<p>Here’s a simple example:</p>
<pre><code class="language-javascript">function isValidInput(text, format) {
  if (format === "EAN13") {
    return /^\d{13}$/.test(text);
  }

  if (format === "UPC") {
    return /^\d{12}$/.test(text);
  }

  return text.length &gt; 0;
}
</code></pre>
<p>Then use it inside your generator:</p>
<pre><code class="language-javascript">if (!isValidInput(text, format)) {
  alert("Invalid input for selected format");
  return;
}
</code></pre>
<p>This ensures users get immediate feedback instead of confusion.</p>
<h2 id="heading-how-to-download-the-barcode">How to Download the Barcode</h2>
<p>Once the barcode is generated, you can allow users to download it.</p>
<pre><code class="language-javascript">function downloadBarcode() {
  const svg = document.getElementById("barcode");
  const serializer = new XMLSerializer();
  const source = serializer.serializeToString(svg);

  const blob = new Blob([source], { type: "image/svg+xml" });
  const url = URL.createObjectURL(blob);

  const link = document.createElement("a");
  link.href = url;
  link.download = "barcode.svg";
  link.click();
}
</code></pre>
<p>This converts the SVG into a file that can be downloaded directly from the browser.</p>
<h2 id="heading-important-notes-from-real-world-use">Important Notes from Real-World Use</h2>
<p>When building tools like this in production, small details matter.</p>
<p>Large input values can sometimes affect readability, so it’s important to test how dense the barcode becomes. Choosing the right format also makes a difference depending on whether you need flexibility or strict standards.</p>
<p>Another important detail is rendering quality. Using SVG instead of raster formats ensures that the barcode remains sharp even when printed.</p>
<h2 id="heading-common-mistakes-to-avoid">Common Mistakes to Avoid</h2>
<p>One common issue is skipping validation. This leads to broken or unreadable barcodes, especially with strict formats like EAN or UPC.</p>
<p>Another mistake is relying too much on button-based interactions. Real-time updates create a much better user experience.</p>
<p>Finally, developers sometimes forget to include the library correctly, which leads to silent failures. Always verify that your CDN is loaded.</p>
<h2 id="heading-demo-how-the-barcode-generator-works">Demo: How the Barcode Generator Works</h2>
<p>To better understand how everything comes together, here’s a quick walkthrough of how the tool works in the browser.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/3a90ba9d-0b4d-4cc6-8060-de238571e67a.png" alt="Barcode generator interface showing barcode type selection options like Code128, EAN-13, UPC and input field for entering barcode data" style="display:block;margin:0 auto" width="944" height="326" loading="lazy">

<h3 id="heading-step-1-select-a-barcode-type">Step 1: Select a Barcode Type</h3>
<p>Start by choosing the barcode format. In most cases, Code128 is a good default since it supports both text and numbers.</p>
<h3 id="heading-step-2-enter-your-data">Step 2: Enter Your Data</h3>
<p>Next, enter the value you want to encode. This could be a product ID, URL, or any text depending on the selected format.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/5e7d1655-92f8-4b38-ac92-52b8fd700aab.png" alt="Barcode customization panel with options to change bar color, background color, width, height, and display settings" style="display:block;margin:0 auto" width="916" height="327" loading="lazy">

<h3 id="heading-step-3-customize-the-design">Step 3: Customize the Design</h3>
<p>You can adjust things like bar width, height, and colors. These settings help control how the barcode looks and how readable it is in different use cases.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/8b46e3fb-bd4d-41c9-84dc-c91e36656680.png" alt="Generated barcode preview displayed in the browser based on user input" style="display:block;margin:0 auto" width="433" height="217" loading="lazy">

<h3 id="heading-step-4-generate-and-preview">Step 4: Generate and Preview</h3>
<p>As you type or change settings, the barcode updates instantly. This real-time preview makes it easier to experiment and see results immediately.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/24b16194-d38d-4e2c-be1a-cbcef646ef7f.png" alt="Download options for generated barcode in PNG, JPG, and SVG formats" style="display:block;margin:0 auto" width="440" height="145" loading="lazy">

<h3 id="heading-step-5-download-the-barcode">Step 5: Download the Barcode</h3>
<p>Once you're satisfied with the result, you can download the barcode in formats like PNG, JPG, or SVG.</p>
<p>This entire process happens in the browser, without uploading any data to a server.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you built a browser-based barcode generator using JavaScript.</p>
<p>More importantly, you learned how to think about building tools that run entirely on the client side. This approach reduces complexity, improves performance, and gives users a faster experience.</p>
<p>Once you understand this pattern, you can apply it to many other tools like QR generators, image converters, and file processors.</p>
<p>And that’s where things start to get interesting.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Browser-Based Image Converter with JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Image conversion is one of those small tasks developers run into occasionally. You might need to convert a PNG to JPEG to reduce size, or export an image to WebP for better performance. Most developer ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-browser-based-image-converter-using-javascript/</link>
                <guid isPermaLink="false">69c173e230a9b81e3a7df8f3</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bhavin Sheth ]]>
                </dc:creator>
                <pubDate>Mon, 23 Mar 2026 17:09:54 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/c042a600-ec0e-495b-b004-dd5a4dfb1434.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Image conversion is one of those small tasks developers run into occasionally. You might need to convert a PNG to JPEG to reduce size, or export an image to WebP for better performance.</p>
<p>Most developers use online tools for this. But there’s a problem: many of those tools upload your image to a server. That can be slow, and sometimes you don’t want to upload private files at all.</p>
<p>The good news is that modern browsers are powerful enough to handle image conversion locally using JavaScript.</p>
<p>In this tutorial, you’ll learn how to build a browser-based image converter that runs entirely in the browser. The tool converts images using JavaScript without uploading files to a server, and allows users to download the converted file instantly.</p>
<p>By the end, you’ll understand how browser-based file processing works and how to use it in your own projects.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a href="#heading-how-browser-based-image-conversion-works">How Browser-Based Image Conversion Works</a></p>
</li>
<li><p><a href="#heading-project-setup">Project Setup</a></p>
</li>
<li><p><a href="#heading-how-to-read-the-image-file-in-javascript">How to Read the Image File in JavaScript</a></p>
</li>
<li><p><a href="#heading-how-the-canvas-converts-the-image">How the Canvas Converts the Image</a></p>
</li>
<li><p><a href="#heading-how-the-download-works">How the Download Works</a></p>
</li>
<li><p><a href="#heading-why-this-approach-is-powerful">Why This Approach Is Powerful</a></p>
</li>
<li><p><a href="#heading-important-notes-from-real-world-use">Important Notes from Real-World Use</a></p>
</li>
<li><p><a href="#heading-common-mistakes-to-avoid">Common Mistakes to Avoid</a></p>
</li>
<li><p><a href="#heading-how-you-can-extend-this-project">How You Can Extend This Project</a></p>
</li>
<li><p><a href="#heading-why-browser-based-tools-are-becoming-more-popular">Why Browser-Based Tools Are Becoming More Popular</a></p>
</li>
<li><p><a href="#heading-demo-how-the-image-converter-works">Demo: How the Image Converter Works</a></p>
</li>
<li><p><a href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-how-browser-based-image-conversion-works">How Browser-Based Image Conversion Works</h2>
<p>Before writing code, you should understand what’s happening behind the scenes.</p>
<p>Modern browsers provide several APIs that make this possible. JavaScript can read local files from a user’s device, draw images on a canvas element, and export the processed image in a different format.</p>
<p>The key pieces we’ll use are:</p>
<ul>
<li><p>File input – to select an image</p>
</li>
<li><p>FileReader – to read the file</p>
</li>
<li><p>Canvas API – to redraw and convert</p>
</li>
<li><p>toDataURL or toBlob – to export the converted image</p>
</li>
</ul>
<p>The important thing is that everything happens locally in the user’s browser. Nothing gets uploaded anywhere.</p>
<h2 id="heading-project-setup">Project Setup</h2>
<p>We’ll keep this simple with just HTML and JavaScript.</p>
<p>Create an <code>index.html</code> file:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;Image Converter&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;h2&gt;Browser Image Converter&lt;/h2&gt;

&lt;input type="file" id="upload" accept="image/*"&gt;

&lt;select id="format"&gt;
  &lt;option value="image/png"&gt;PNG&lt;/option&gt;
  &lt;option value="image/jpeg"&gt;JPEG&lt;/option&gt;
  &lt;option value="image/webp"&gt;WebP&lt;/option&gt;
&lt;/select&gt;

&lt;button onclick="convertImage()"&gt;Convert&lt;/button&gt;

&lt;br&gt;&lt;br&gt;

&lt;a id="download" style="display:none;"&gt;Download Converted Image&lt;/a&gt;

&lt;script src="script.js"&gt;&lt;/script&gt;

&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>This simple interface includes a file upload input for selecting the image, a format selector for choosing the output format, a convert button to start the process, and a download link that appears once the image has been converted.</p>
<p>Now let’s add the logic.</p>
<h2 id="heading-how-to-read-the-image-file-in-javascript">How to Read the Image File in JavaScript</h2>
<p>Create a <code>script.js</code> file:</p>
<pre><code class="language-javascript">function convertImage() {

  const fileInput = document.getElementById("upload");
  const format = document.getElementById("format").value;

  if (!fileInput.files.length) {
    alert("Please select an image");
    return;
  }

  const file = fileInput.files[0];
  const reader = new FileReader();

  reader.onload = function(event) {

    const img = new Image();

    img.onload = function() {

      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");

      canvas.width = img.width;
      canvas.height = img.height;

      ctx.drawImage(img, 0, 0);

      const converted = canvas.toDataURL(format);

      const link = document.getElementById("download");

      link.href = converted;
      link.download = "converted-image";
      link.style.display = "inline";
      link.innerText = "Download Converted Image";

    };

    img.src = event.target.result;

  };

  reader.readAsDataURL(file);

}
</code></pre>
<p>This is the core of the image converter. Let’s break down what’s happening.</p>
<h3 id="heading-how-the-canvas-converts-the-image">How the Canvas Converts the Image</h3>
<p>This line draws the image:</p>
<pre><code class="language-javascript">ctx.drawImage(img, 0, 0);
</code></pre>
<p>Now the image exists inside the canvas.</p>
<p>This line converts it:</p>
<pre><code class="language-javascript">canvas.toDataURL(format);
</code></pre>
<p>This exports the image in the selected format.</p>
<p>For example:</p>
<ul>
<li><p>PNG → image/png</p>
</li>
<li><p>JPEG → image/jpeg</p>
</li>
<li><p>WebP → image/webp</p>
</li>
</ul>
<p>This is where the conversion actually happens.</p>
<h3 id="heading-how-the-download-works"><strong>How the Download Works</strong></h3>
<p>This part creates the download:</p>
<pre><code class="language-javascript">link.href = converted;
link.download = "converted-image";
</code></pre>
<p>The browser treats it as a downloadable file. No server needed.</p>
<h3 id="heading-why-this-approach-is-powerful"><strong>Why This Approach Is Powerful</strong></h3>
<p>This technique has several advantages.</p>
<ul>
<li><p><strong>It’s fast</strong>: There is no upload time, and everything runs locally.</p>
</li>
<li><p><strong>It’s private</strong>: Files never leave the user’s device. This matters for sensitive images.</p>
</li>
<li><p><strong>It reduces server costs</strong>: You don’t need backend processing. No storage, and no processing servers.</p>
</li>
</ul>
<h2 id="heading-important-notes-from-real-world-use">Important Notes from Real-World Use</h2>
<p>If you plan to build tools like this, here are a few practical things I’ve learned.</p>
<h3 id="heading-large-images-use-more-memory">Large Images Use More Memory</h3>
<p>Very large images can slow down the browser. If needed, you can resize images using Canvas.</p>
<h3 id="heading-jpeg-supports-quality-settings">JPEG Supports Quality Settings</h3>
<p>You can control quality:</p>
<pre><code class="language-plaintext">canvas.toDataURL("image/jpeg", 0.8);
</code></pre>
<p>This reduces file size.</p>
<h3 id="heading-webp-usually-gives-the-best-compression">WebP Usually Gives the Best Compression</h3>
<p>WebP often produces smaller files than PNG or JPEG. It’s a good default option.</p>
<h3 id="heading-how-to-resize-an-image-using-canvas">How to Resize an Image Using Canvas</h3>
<p>If you need to reduce the size of large images, you can resize them before exporting.</p>
<p>After loading the image, you can set a smaller width and height on the canvas:</p>
<pre><code class="language-javascript">const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");

const maxWidth = 800;
const scale = maxWidth / img.width;

canvas.width = maxWidth;
canvas.height = img.height * scale;

ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
</code></pre>
<h2 id="heading-common-mistakes-to-avoid">Common Mistakes to Avoid</h2>
<h3 id="heading-trying-to-upload-files-unnecessarily">Trying to Upload Files Unnecessarily</h3>
<p>If processing can happen in the browser, do it there. It’s faster and simpler.</p>
<h3 id="heading-forgetting-browser-compatibility">Forgetting Browser Compatibility</h3>
<p>Most modern browsers support Canvas and FileReader. But always test.</p>
<h3 id="heading-not-validating-file-input-properly">Not Validating File Input Properly</h3>
<p>Before processing the image, it’s important to validate the input file.</p>
<p>For example, you can check if a file is selected and ensure it is an image:</p>
<pre><code class="language-javascript">const file = fileInput.files[0];

if (!file) {
  alert("Please select a file.");
  return;
}

if (!file.type.startsWith("image/")) {
  alert("Please upload a valid image file.");
  return;
}
</code></pre>
<h2 id="heading-how-you-can-extend-this-project">How You Can Extend This Project</h2>
<p>Once this basic converter works, you can expand it with additional features. For example, you could add image resizing so users can adjust dimensions before downloading the converted file. Another useful improvement is implementing drag-and-drop uploads, which makes the interface more user-friendly.</p>
<p>You might also support multiple file uploads so users can convert several images at once. Adding compression controls would allow users to balance image quality and file size. Finally, you could include an image preview before download so users can confirm the result before saving the file.</p>
<p>All of these improvements rely on the same browser APIs used in this tutorial, so once you understand the core logic, extending the project becomes much easier.</p>
<h2 id="heading-why-browser-based-tools-are-becoming-more-popular">Why Browser-Based Tools Are Becoming More Popular</h2>
<p>Browsers today are far more capable than they used to be. Modern browser APIs allow developers to handle tasks that previously required server-side processing.</p>
<p>For example, browsers can now perform image processing, generate PDFs, convert files into different formats, and even handle some types of video processing directly on the client side.</p>
<p>Because of these capabilities, developers can build tools that run entirely inside the browser without relying on a backend server. This approach improves performance since users don’t have to upload files and wait for a server to process them.</p>
<p>It also improves privacy because files stay on the user’s device instead of being sent to a remote server. At the same time, it simplifies system architecture and makes applications easier to scale since there is no server infrastructure needed for file processing.</p>
<h2 id="heading-demo-how-the-image-converter-works">Demo: How the Image Converter Works</h2>
<p>After building the project, here is what the tool looks like in the browser.</p>
<h3 id="heading-upload-an-image">Upload an Image</h3>
<p>First, the user uploads an image using the file upload area.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/a11a29c5-32ff-4a08-a2bb-78a672ccde41.png" alt="Image upload interface showing drag and drop area" style="display:block;margin:0 auto" width="1603" height="608" loading="lazy">

<h3 id="heading-select-the-output-format">Select the Output Format</h3>
<p>After uploading the image, the tool displays a preview along with details such as the <strong>image name, format, and file size</strong>. This helps users confirm that they uploaded the correct file before converting it.</p>
<p>Next, the user can choose the desired output format from the dropdown menu. The tool supports formats such as <strong>PNG, JPEG, WebP, GIF, and BMP</strong>, allowing the image to be converted into the format that best fits the user's needs.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/9be730b3-f9d0-4cb4-b110-5cb5bddddbb2.png" alt="Dropdown menu for selecting output image format" style="display:block;margin:0 auto" width="738" height="854" loading="lazy">

<h3 id="heading-convert-the-image">Convert the Image</h3>
<p>Once the format is selected, clicking the <strong>Convert All Images</strong> button processes the image directly in the browser.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/bd54a90b-ddf0-4875-aec6-a414d5f9c421.png" alt="convert button used to process for the uplaoded imnage" style="display:block;margin:0 auto" width="717" height="64" loading="lazy">

<h3 id="heading-download-the-converted-image">Download the Converted Image</h3>
<p>After conversion is complete, the tool generates a downloadable file.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/5c4ef749-9e0e-45f5-9a78-275866f10dfc.png" alt="converted image with download option" style="display:block;margin:0 auto" width="831" height="724" loading="lazy">

<h3 id="heading-conversion-results">Conversion Results</h3>
<p>The tool can also display useful information such as original size, converted size, and space saved after compression.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6979d22f93bc273cc33971b1/9c58dfc2-5b0b-4f18-a782-aea4e0fc868c.png" alt="image conversion result showing file size reduction " style="display:block;margin:0 auto" width="815" height="199" loading="lazy">

<p>Because everything happens in the browser using JavaScript and the Canvas API, the image never leaves the user's device.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you built a browser-based image converter using JavaScript.</p>
<p>In this tutorial, you learned how to read local image files using JavaScript, process images using the Canvas API, convert them into different formats, and allow users to download the result directly from the browser.</p>
<p>This pattern is useful far beyond image conversion.</p>
<p>You can use the same approach for many browser-based tools.</p>
<p>Understanding how to use browser APIs like this opens up a lot of possibilities for building fast, efficient web applications.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Run Rust on Jupyter Notebooks ]]>
                </title>
                <description>
                    <![CDATA[ If you've ever wanted to combine the power of Rust with the interactive goodness of Jupyter notebooks, you're in the right place. Maybe you're tired of compiling every single time you want to test a s ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-run-rust-on-jupyter-notebooks/</link>
                <guid isPermaLink="false">699879483dc17c4862f498f9</guid>
                
                    <category>
                        <![CDATA[ Rust ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Jupyter Notebook  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Linux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ WSL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Daniel Iwugo ]]>
                </dc:creator>
                <pubDate>Fri, 20 Feb 2026 15:10:00 +0000</pubDate>
                <media:content url="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/5e1e335a7a1d3fcc59028c64/6e411f5d-65a1-407d-a4f0-0beceb1e784b.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you've ever wanted to combine the power of Rust with the interactive goodness of Jupyter notebooks, you're in the right place. Maybe you're tired of compiling every single time you want to test a snippet, learn Rust in a more interactive way, or just have a crazy idea pop into your head like I do.</p>
<p>Most people think Jupyter is just for Python and data science stuff, but apparently you can run Rust in one, too.</p>
<p>In this tutorial, we’ll be taking a look at:</p>
<ol>
<li><p><a href="#heading-what-is-evcxr">What is EvCxR?</a></p>
</li>
<li><p><a href="#heading-how-to-install-the-rust-jupyter-kernel">How to Install the Rust Jupyter kernel</a></p>
</li>
<li><p><a href="#heading-step-4-write-your-first-rust-code">How to run your first Rust code in a notebook</a></p>
</li>
<li><p><a href="#heading-handy-tips-and-tricks">Handy Tips and Tricks</a></p>
</li>
<li><p><a href="#heading-common-issues-and-solutions">Common Issues and Solutions</a></p>
</li>
<li><p><a href="#heading-when-not-to-use-jupyter-for-rust">When NOT to Use Jupyter for Rust</a></p>
</li>
</ol>
<p><strong>Friendly Disclaimer</strong>: This tutorial assumes you know the basics of both Rust and Jupyter. If you break something, that's on you, mate 🙂.</p>
<p>So without further ado, let's jump in.</p>
<h2 id="heading-what-is-evcxr"><strong>What is EvCxR?</strong></h2>
<p>EvCxR (pronounced "Evaluator" to my fellow linguists’ horror) is a Rust REPL and Jupyter kernel. It's basically the magic that lets you run Rust code interactively in Jupyter notebooks instead of the traditional compile-run-debug cycle.</p>
<p>The name stands for "Evaluation Context for Rust", and it’s an open source project actively maintained on GitHub. Here are a few things that make this terribly named tool absolutely brilliant:</p>
<ol>
<li><p><strong>Interactive development:</strong> It lets you test Rust snippets without creating a whole project 🧪</p>
</li>
<li><p><strong>Prototyping:</strong> You can quickly try out ideas before committing to a full implementation 💡</p>
</li>
<li><p><strong>Data visualisation:</strong> And yes, you can even plot charts with Rust (more on that later) 📊</p>
</li>
</ol>
<h2 id="heading-how-to-install-the-rust-jupyter-kernel">How to Install the Rust Jupyter kernel</h2>
<h3 id="heading-prerequisites"><strong>Prerequisites</strong></h3>
<p>Before we dive into the installation, make sure you have these sorted:</p>
<ol>
<li><p><strong>A Linux System:</strong> Or at least, Windows Subsystem for Linux (There’s a little note below for Windows users.)</p>
</li>
<li><p><strong>The Rust toolchain:</strong> You can get it from <a href="https://rustup.rs/">rustup.rs</a> if you haven't already</p>
</li>
<li><p><strong>Jupyter:</strong> Install via pip – <code>pip install jupyter</code></p>
</li>
<li><p><strong>Patience:</strong> This might take a minute or two ⏱️</p>
</li>
</ol>
<p>Once you’ve got all that, we can get rusty (pun intended).</p>
<p><strong>Note:</strong> If you’re using Windows, you’ll need to do a little extra to get started. Here’s the quick rundown:</p>
<ol>
<li><p>Go to <a href="https://visualstudio.microsoft.com/visual-cpp-build-tools/">https://visualstudio.microsoft.com/visual-cpp-build-tools/</a></p>
</li>
<li><p>Download and run the installer</p>
</li>
<li><p>Select <strong>"Desktop development with C++"</strong></p>
</li>
<li><p>Install it (it's large, ~5GB)</p>
</li>
</ol>
<h3 id="heading-step-1-install-evcxr"><strong>Step 1: Install EvCxR</strong></h3>
<p>Open your terminal and run this command:</p>
<pre><code class="language-rust">cargo install evcxr_jupyter
</code></pre>
<p>Now go grab a cup of joe ☕. This will take a few minutes as Cargo downloads and compiles everything. And don't panic if it seems stuck. Rust compilation is thorough but not particularly fast.</p>
<p>If you get any errors about missing system libraries, you might need to install some dependencies. On Ubuntu/Debian, try:</p>
<pre><code class="language-bash">sudo apt install jupyter-notebook jupyter-core python-ipykernel
sudo apt install cmake
</code></pre>
<p>On macOS with Homebrew:</p>
<pre><code class="language-bash">brew install cmake jupyter
</code></pre>
<h3 id="heading-step-2-install-the-jupyter-kernel"><strong>Step 2: Install the Jupyter Kernel</strong></h3>
<p>Once the installation finishes, you’ll need to register the EvCxR kernel with Jupyter:</p>
<pre><code class="language-bash">evcxr_jupyter --install
</code></pre>
<p>You should see output that looks something like this at the end:</p>
<pre><code class="language-plaintext">Installation complete
</code></pre>
<h3 id="heading-step-3-launch-jupyter-and-create-a-rust-notebook"><strong>Step 3: Launch Jupyter and Create a Rust Notebook</strong></h3>
<p>Let’s test out our baby. Fire up Jupyter:</p>
<pre><code class="language-bash">jupyter notebook
</code></pre>
<p>Your browser should open automatically (if it doesn't, copy the URL from the terminal).</p>
<p>In the Jupyter interface:</p>
<ol>
<li><p>Click <strong>New</strong> in the top right</p>
</li>
<li><p>Select <strong>Rust</strong> from the dropdown (or "evcxr" depending on your version)</p>
</li>
<li><p>A new notebook opens</p>
</li>
</ol>
<p>Welcome to interactive Rust! 🦀</p>
<h3 id="heading-step-4-write-your-first-rust-code"><strong>Step 4: Write Your First Rust Code</strong></h3>
<p>Let's start with a classic:</p>
<pre><code class="language-rust">println!("Hello my fellow Rustaceans! 🦀");
</code></pre>
<p>Hit <code>Shift + Enter</code> to run the cell. You should see the output appear below the cell. Simple as that.</p>
<p>Note that notebooks execute code at the top level, so you don’t have to wrap it around the <code>main()</code> function. If you still want to do that, you’re going to have to call it like this:</p>
<pre><code class="language-rust">fn main(){
    println!("Hello my fellow Rustaceans! 🦀");
}
//Calling the function
main()
</code></pre>
<p>Now let's try something more interesting:</p>
<pre><code class="language-rust">fn fibonacci(n: u32) -&gt; u32 {
    match n {
        0 =&gt; 0,
        1 =&gt; 1,
        _ =&gt; fibonacci(n - 1) + fibonacci(n - 2)
    }
}

for i in 0..10 {
    println!("fibonacci({}) = {}", i, fibonacci(i));
}
</code></pre>
<p>Run it and watch the Fibonacci sequence appear.</p>
<pre><code class="language-plaintext">fibonacci(0) = 0
fibonacci(1) = 1
fibonacci(2) = 1
fibonacci(3) = 2
fibonacci(4) = 3
fibonacci(5) = 5
fibonacci(6) = 8
fibonacci(7) = 13
fibonacci(8) = 21
fibonacci(9) = 34
</code></pre>
<h2 id="heading-handy-tips-and-tricks"><strong>Handy Tips and Tricks</strong></h2>
<p>Functions aren’t the only things that behave differently when using Rust in notebooks. Here are a few other things you might want to keep in mind:</p>
<h3 id="heading-variables-persist-between-cells">Variables Persist Between Cells</h3>
<p>Unlike traditional Rust compilation, variables you define in one cell stick around for the next cells:</p>
<pre><code class="language-rust">let mut counter = 0;
</code></pre>
<p>Then in the next cell:</p>
<pre><code class="language-rust">counter += 1;
println!("Counter: {}", counter);
</code></pre>
<p>The output would be:</p>
<pre><code class="language-plaintext">Counter: 1
</code></pre>
<p>This is great for building up complex examples step by step.</p>
<h3 id="heading-you-can-use-external-crates">You Can Use External Crates</h3>
<p>Add dependencies with the <code>:dep</code> command in one cell:</p>
<pre><code class="language-rust">:dep serde = { version = "1.0", features = ["derive"] }
:dep serde_json = "1.0"
</code></pre>
<p>Then use them normally in the next:</p>
<pre><code class="language-rust">use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize, Debug)]
struct Person {
    name: String,
    age: u32,
}

let person = Person {
    name: "Amina".to_string(),
    age: 24,
};

let json = serde_json::to_string(&amp;person).unwrap();
println!("{}", json);
</code></pre>
<p>Output:</p>
<pre><code class="language-plaintext">{"name":"Amina","age":24}
</code></pre>
<p>Pretty neat, huh?</p>
<h3 id="heading-visualisation-support">Visualisation Support</h3>
<p>You can even create graphs. To get started, install the <code>plotters</code> crate:</p>
<pre><code class="language-rust">:dep plotters = { version = "0.3", default-features = false, features = ["evcxr", "all_series", "bitmap_backend", "bitmap_encoder"] }
</code></pre>
<p>Then create a simple sine graph:</p>
<pre><code class="language-rust">use plotters::prelude::*;

let root = SVGBackend::new("sine_wave.svg", (640, 480)).into_drawing_area();
root.fill(&amp;WHITE).unwrap();

let mut chart = ChartBuilder::on(&amp;root)
    .caption("Sine Wave", ("Arial", 20))
    .margin(5)
    .x_label_area_size(30)
    .y_label_area_size(30)
    .build_cartesian_2d(-3.14..3.14, -1.2..1.2)
    .unwrap();

chart.configure_mesh().draw().unwrap();

chart.draw_series(LineSeries::new(
    (-314..314).map(|x| {
        let x = x as f64 / 100.0;
        (x, x.sin())
    }),
    &amp;RED,
)).unwrap();

root.present().unwrap();
println!("Plot saved to sine_wave.svg");
</code></pre>
<p>Output:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771272251271/c07b1c22-4ea1-408c-984a-4179a47058d9.png" alt="Sine wave graph showing output of the code" style="display:block;margin:0 auto" width="640" height="480" loading="lazy">

<p><strong>A word on plotting:</strong> You can actually display plots directly inline in your notebook. But if you're using WSL with VSCode (like I do), inline plotting may not work properly due to rendering issues on the notebook interface. That’s why I used it as an svg file that I can easily view in my text editor.</p>
<h3 id="heading-checking-types">Checking Types</h3>
<p>Not sure what type something is? Use <code>:vars</code>. This shows all variables and their types:</p>
<pre><code class="language-rust">let x = vec![1, 2, 3];
</code></pre>
<pre><code class="language-rust">:vars
</code></pre>
<p>Output:</p>
<pre><code class="language-plaintext">Variable	    Type
       x	Vec&lt;i32&gt;
</code></pre>
<h2 id="heading-common-issues-and-solutions">Common Issues and Solutions</h2>
<h3 id="heading-compilation-errors-everywhere">Compilation Errors Everywhere</h3>
<p>If you're getting weird compilation errors, remember:</p>
<ul>
<li><p>Each cell is compiled separately</p>
</li>
<li><p>You might need to reimport things in each cell</p>
</li>
</ul>
<h3 id="heading-slow-execution">Slow Execution</h3>
<p>The first time you run code in a session, it's slow due to the compilation overhead. Subsequent runs are faster. If it's really slow, you might want to:</p>
<ul>
<li><p>Use release mode: <code>:opt 2</code></p>
</li>
<li><p>Reduce dependency features to only what you need</p>
</li>
<li><p>Consider if Jupyter is the right tool for your use case</p>
</li>
</ul>
<h3 id="heading-dependencies-not-loading">Dependencies Not Loading</h3>
<p>If a crate won't load:</p>
<ul>
<li><p>Make sure the version exists on <a href="http://crates.io">crates.io</a></p>
</li>
<li><p>Check your internet connection (it needs to download)</p>
</li>
<li><p>Try specifying features explicitly</p>
</li>
<li><p>Clear the cargo cache if things get really wonky: <code>rm -rf ~/.evcxr</code></p>
</li>
</ul>
<h2 id="heading-when-not-to-use-jupyter-for-rust"><strong>When NOT to Use Jupyter for Rust</strong></h2>
<p>Jupyter notebooks are great for learning and experimenting, but they're not always the best choice in:</p>
<ul>
<li><p><strong>Production code:</strong> Use proper projects with cargo</p>
</li>
<li><p><strong>Performance-critical code:</strong> The overhead isn't worth it</p>
</li>
<li><p><strong>Large applications:</strong> Notebooks get very messy, very fast</p>
</li>
<li><p><strong>Team collaboration:</strong> Version control with notebooks is quite the nightmare</p>
</li>
</ul>
<p>Stick to notebooks for prototyping and quick experiments. For anything serious, fire up your favourite editor and create a proper Rust project.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Let's summarise what you've learned:</p>
<ol>
<li><p>How to install the EvCxR Jupyter kernel</p>
</li>
<li><p>How to create and run Rust notebooks</p>
</li>
<li><p>How to use external crates in notebooks</p>
</li>
<li><p>Tips and tricks for interactive Rust development</p>
</li>
</ol>
<p>Jupyter notebooks make Rust more accessible for learning and experimentation. Give it a go next time you want to try out a quick Rust snippet without the ceremony of creating a full project. And with that, we've come to the end of this tutorial.</p>
<p>Cheers.</p>
<h2 id="heading-resources">Resources</h2>
<ol>
<li><p><a href="https://github.com/evcxr/evcxr">EvCxR GitHub Repository</a></p>
</li>
<li><p><a href="https://doc.rust-lang.org/book/">Rust Book</a></p>
</li>
<li><p><a href="https://jupyter.org/documentation">Jupyter Documentation</a></p>
</li>
</ol>
<h2 id="heading-acknowledgements">Acknowledgements</h2>
<p>Thanks to <a href="https://www.linkedin.com/in/a-n-u-o/">Anuoluwapo Victor</a>, <a href="https://www.linkedin.com/in/a-n-u-o/">Chinaza Nwukwa,</a> <a href="https://www.linkedin.com/in/chinaza-nwukwa-22a256230/">Holumidey Mer</a><a href="https://www.linkedin.com/in/mercy-holumidey-88a542232/">cy</a>, <a href="https://www.linkedin.com/in/mercy-holumidey-88a542232/">Favour Ojo,</a> <a href="https://www.linkedin.com/in/favour-ojo-906883199/">Georgina</a> <a href="https://www.linkedin.com/in/georgina-awani-254974233/">Awani</a>, <a href="https://www.linkedin.com/in/georgina-awani-254974233/">and my family</a> for the inspiration, support and knowledge used to put this post together.</p>
<p>And thanks to the EvCxR project maintainers for making this possible, the Rust community for being awesome, and to anyone reading this for wanting to learn. You inspire me daily.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Transformers for Real-Time Gesture Recognition ]]>
                </title>
                <description>
                    <![CDATA[ Gesture and sign recognition is a growing field in computer vision, powering accessibility tools and natural user interfaces. Most beginner projects rely on hand landmarks or small CNNs, but these often miss the bigger picture because gestures are no... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/using-transformers-for-real-time-gesture-recognition/</link>
                <guid isPermaLink="false">68e3c692aa82abf4b593114c</guid>
                
                    <category>
                        <![CDATA[ Computer Vision ]]>
                    </category>
                
                    <category>
                        <![CDATA[ transformers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ pytorch ]]>
                    </category>
                
                    <category>
                        <![CDATA[ ONNX ]]>
                    </category>
                
                    <category>
                        <![CDATA[ gradio ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Machine Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Deep Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Gesture Recognition ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Accessibility ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ OMOTAYO OMOYEMI ]]>
                </dc:creator>
                <pubDate>Mon, 06 Oct 2025 13:39:30 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759757931295/5f19fd4e-93c0-4bd7-a75c-a7858e061ecd.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Gesture and sign recognition is a growing field in computer vision, powering accessibility tools and natural user interfaces. Most beginner projects rely on hand landmarks or small CNNs, but these often miss the bigger picture because gestures are not static images. Rather, they unfold over time. To build more robust, real-time systems, we need models that can capture both spatial details and temporal context.</p>
<p>This is where Transformers come in. Originally built for language, they’ve become state-of-the-art in vision tasks thanks to models like the Vision Transformer (ViT) and video-focused variants such as TimeSformer.</p>
<p>In this tutorial, we’ll use a Transformer backbone to create a lightweight real-time gesture recognition tool, optimized for small datasets and deployable on a regular laptop webcam.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-transformers-for-gestures">Why Transformers for Gestures?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-youll-learn">What You’ll Learn</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-project-setup">Project Setup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-generate-a-gesture-dataset">Generate a Gesture Dataset</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-option-1-generate-a-synthetic-dataset">Option 1: Generate a Synthetic Dataset</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-training-script-trainpy">Training Script:</a> <a target="_blank" href="http://train.py">train.py</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-export-the-model-to-onnx">Export the Model to ONNX</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-evaluate-accuracy-latency">Evaluate Accuracy + Latency</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-option-2-use-small-samples-from-public-gesture-datasets">Option 2: Use Small Samples from Public Gesture Datasets</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-accessibility-notes-amp-ethical-limits">Accessibility Notes &amp; Ethical Limits</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-next-steps">Next Steps</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-why-transformers-for-gestures">Why Transformers for Gestures?</h2>
<p>Transformers are powerful because they use self-attention to model relationships across a sequence. For gestures, this means the model doesn’t just see isolated frames, but also learns how movements evolve over time. A wave, for example, looks different from a raised hand only when viewed as a sequence.</p>
<p>Vision Transformers process images as patches, while video Transformers extend this to multiple frames with temporal attention. Even a simple approach, like applying ViT to each frame and pooling across time, can outperform traditional CNN-based methods for small datasets.</p>
<p>Combined with Hugging Face’s pre-trained models and ONNX Runtime for optimization, Transformers make it possible to train on a modest dataset and still achieve smooth real-time recognition.</p>
<h2 id="heading-what-youll-learn">What You’ll Learn</h2>
<p>In this tutorial, you’ll build a gesture recognition system using Transformers. By the end, you’ll know how to:</p>
<ul>
<li><p>Create (or record) a tiny gesture dataset</p>
</li>
<li><p>Train a Vision Transformer (ViT) with temporal pooling</p>
</li>
<li><p>Export the model to ONNX for faster inference</p>
</li>
<li><p>Build a real-time Gradio app that classifies gestures from your webcam</p>
</li>
<li><p>Evaluate your model’s accuracy and latency with simple scripts</p>
</li>
<li><p>Understand the accessibility potential and ethical limits of gesture recognition</p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along, you should have:</p>
<ul>
<li><p>Basic Python knowledge (functions, scripts, virtual environments)</p>
</li>
<li><p>Familiarity with PyTorch (tensors, datasets, training loops) – helpful but not required</p>
</li>
<li><p>Python 3.8+ installed on your system</p>
</li>
<li><p>A webcam (for the live demo in Gradio)</p>
</li>
<li><p>Optionally: GPU access (training on CPU works, but is slower)</p>
</li>
</ul>
<h2 id="heading-project-setup">Project Setup</h2>
<p>Create a new project folder and install the required libraries.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a new project directory and navigate into it</span>
mkdir transformer-gesture &amp;&amp; <span class="hljs-built_in">cd</span> transformer-gesture

<span class="hljs-comment"># Set up a Python virtual environment</span>
python -m venv .venv

<span class="hljs-comment"># Activate the virtual environment</span>
<span class="hljs-comment"># Windows PowerShell</span>
.venv\Scripts\Activate.ps1

<span class="hljs-comment"># macOS/Linux</span>
<span class="hljs-built_in">source</span> .venv/bin/activate
</code></pre>
<p>The provided code snippet is a set of commands for setting up a new Python project with a virtual environment. Here's a breakdown of each part:</p>
<ol>
<li><p><code>mkdir transformer-gesture &amp;&amp; cd transformer-gesture</code>: This command creates a new directory named "transformer-gesture" and then navigates into it.</p>
</li>
<li><p><code>python -m venv .venv</code>: This command creates a new virtual environment in the current directory. The virtual environment is stored in a folder named ".venv".</p>
</li>
<li><p>Activating the virtual environment:</p>
<ul>
<li><p>For Windows PowerShell, you can use <code>.venv\Scripts\Activate.ps1</code> to activate the virtual environment.</p>
</li>
<li><p>For macOS/Linux, use <code>source .venv/bin/activate</code> to activate the virtual environment.</p>
</li>
</ul>
</li>
</ol>
<p>Activating a virtual environment ensures that the Python interpreter and any packages you install are isolated to this specific project, preventing conflicts with other projects or system-wide packages.</p>
<p>Create a <code>requirements.txt</code> file:</p>
<pre><code class="lang-plaintext">torch&gt;=2.0
torchvision
torchaudio
timm
huggingface_hub

onnx
onnxruntime

gradio

numpy
opencv-python
pillow

matplotlib
seaborn
scikit-learn
</code></pre>
<p>The list provided is a set of package dependencies typically found in a <code>requirements.txt</code> file for a Python project. Here's a brief explanation of each package:</p>
<ol>
<li><p><strong>torch&gt;=2.0</strong>: PyTorch is a popular open-source deep learning framework that provides a flexible and efficient platform for building and training neural networks. Version 2.0 and above includes improvements in performance and new features.</p>
</li>
<li><p><strong>torchvision</strong>: This library is part of the PyTorch ecosystem and provides tools for computer vision tasks, including datasets, model architectures, and image transformations.</p>
</li>
<li><p><strong>torchaudio</strong>: Also part of the PyTorch ecosystem, Torchaudio provides audio processing tools and datasets, making it easier to work with audio data in deep learning projects.</p>
</li>
<li><p><strong>timm</strong>: The PyTorch Image Models (timm) library offers a collection of pre-trained models and utilities for computer vision tasks, facilitating quick experimentation and deployment.</p>
</li>
<li><p><strong>huggingface_hub</strong>: This library allows easy access to models and datasets hosted on the Hugging Face Hub, a platform for sharing and collaborating on machine learning models and datasets.</p>
</li>
<li><p><strong>onnx</strong>: The Open Neural Network Exchange (ONNX) format is used to represent machine learning models, enabling interoperability between different frameworks.</p>
</li>
<li><p><strong>onnxruntime</strong>: This is a high-performance runtime for executing ONNX models, allowing for efficient deployment across various platforms.</p>
</li>
<li><p><strong>gradio</strong>: Gradio is a library for creating user interfaces for machine learning models, making them accessible through a web interface for easy interaction and testing.</p>
</li>
<li><p><strong>numpy</strong>: A fundamental package for numerical computing in Python, providing support for arrays and a wide range of mathematical functions.</p>
</li>
<li><p><strong>opencv-python</strong>: OpenCV is a library for computer vision and image processing tasks, widely used for real-time applications.</p>
</li>
<li><p><strong>pillow</strong>: A Python Imaging Library (PIL) fork, Pillow provides tools for opening, manipulating, and saving many different image file formats.</p>
</li>
<li><p><strong>matplotlib</strong>: A plotting library for Python, Matplotlib is used for creating static, interactive, and animated visualizations in Python.</p>
</li>
<li><p><strong>seaborn</strong>: Built on top of Matplotlib, Seaborn provides a high-level interface for drawing attractive and informative statistical graphics.</p>
</li>
<li><p><strong>scikit-learn</strong>: A machine learning library in Python that provides simple and efficient tools for data analysis and modeling, including classification, regression, clustering, and dimensionality reduction.</p>
</li>
</ol>
<p>Install dependencies:</p>
<pre><code class="lang-bash">pip install -r requirements.txt
</code></pre>
<p>The command <code>pip install -r requirements.txt</code> is used to install all the Python packages listed in a file named <code>requirements.txt</code>. This file typically contains a list of package dependencies required for a Python project, each specified with a package name and optionally a version number.</p>
<p>By running this command, <code>pip</code>, which is the Python package installer, reads the file and installs each package listed, ensuring that the project has all the necessary dependencies to run properly. This is a common practice in Python projects to manage and share dependencies easily.</p>
<h2 id="heading-generate-a-gesture-dataset">Generate a Gesture Dataset</h2>
<p>To train our Transformer-based gesture recognizer, we need some data. Instead of downloading a huge dataset, we’ll start with a tiny synthetic dataset you can generate in seconds. This makes the tutorial lightweight and ensures that everyone can follow along without dealing with multi-gigabyte downloads.</p>
<h2 id="heading-option-1-generate-a-synthetic-dataset">Option 1: Generate a Synthetic Dataset</h2>
<p>We’ll use a small Python script that creates short <code>.mp4</code> clips of a moving (or still) coloured box. Each class represents a gesture:</p>
<ul>
<li><p><strong>swipe_left</strong> – box moves from right to left</p>
</li>
<li><p><strong>swipe_right</strong> – box moves from left to right</p>
</li>
<li><p><strong>stop</strong> – box stays still in the center</p>
</li>
</ul>
<p>Save this script as <code>generate_synthetic_gestures.py</code> in your project root:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os, cv2, numpy <span class="hljs-keyword">as</span> np, random, argparse

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">ensure_dir</span>(<span class="hljs-params">p</span>):</span> os.makedirs(p, exist_ok=<span class="hljs-literal">True</span>)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">make_clip</span>(<span class="hljs-params">mode, out_path, seconds=<span class="hljs-number">1.5</span>, fps=<span class="hljs-number">16</span>, size=<span class="hljs-number">224</span>, box_size=<span class="hljs-number">60</span>, seed=<span class="hljs-number">0</span>, codec=<span class="hljs-string">"mp4v"</span></span>):</span>
    rng = random.Random(seed)
    frames = int(seconds * fps)
    H = W = size

    <span class="hljs-comment"># background + box color</span>
    bg_val = rng.randint(<span class="hljs-number">160</span>, <span class="hljs-number">220</span>)
    bg = np.full((H, W, <span class="hljs-number">3</span>), bg_val, dtype=np.uint8)
    color = (rng.randint(<span class="hljs-number">20</span>, <span class="hljs-number">80</span>), rng.randint(<span class="hljs-number">20</span>, <span class="hljs-number">80</span>), rng.randint(<span class="hljs-number">20</span>, <span class="hljs-number">80</span>))

    <span class="hljs-comment"># path of motion</span>
    y = rng.randint(<span class="hljs-number">40</span>, H - <span class="hljs-number">40</span> - box_size)
    <span class="hljs-keyword">if</span> mode == <span class="hljs-string">"swipe_left"</span>:
        x_start, x_end = W - <span class="hljs-number">20</span> - box_size, <span class="hljs-number">20</span>
    <span class="hljs-keyword">elif</span> mode == <span class="hljs-string">"swipe_right"</span>:
        x_start, x_end = <span class="hljs-number">20</span>, W - <span class="hljs-number">20</span> - box_size
    <span class="hljs-keyword">elif</span> mode == <span class="hljs-string">"stop"</span>:
        x_start = x_end = (W - box_size) // <span class="hljs-number">2</span>
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Unknown mode: <span class="hljs-subst">{mode}</span>"</span>)

    fourcc = cv2.VideoWriter_fourcc(*codec)
    vw = cv2.VideoWriter(out_path, fourcc, fps, (W, H))
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> vw.isOpened():
        <span class="hljs-keyword">raise</span> RuntimeError(
            <span class="hljs-string">f"Could not open VideoWriter with codec '<span class="hljs-subst">{codec}</span>'. "</span>
            <span class="hljs-string">"Try --codec XVID and use .avi extension, e.g. out.avi"</span>
        )

    <span class="hljs-keyword">for</span> t <span class="hljs-keyword">in</span> range(frames):
        alpha = t / max(<span class="hljs-number">1</span>, frames - <span class="hljs-number">1</span>)
        x = int((<span class="hljs-number">1</span> - alpha) * x_start + alpha * x_end)
        <span class="hljs-comment"># small jitter to avoid being too synthetic</span>
        jitter_x, jitter_y = rng.randint(<span class="hljs-number">-2</span>, <span class="hljs-number">2</span>), rng.randint(<span class="hljs-number">-2</span>, <span class="hljs-number">2</span>)
        frame = bg.copy()
        cv2.rectangle(frame, (x + jitter_x, y + jitter_y),
                      (x + jitter_x + box_size, y + jitter_y + box_size),
                      color, thickness=<span class="hljs-number">-1</span>)
        <span class="hljs-comment"># overlay text</span>
        cv2.putText(frame, mode, (<span class="hljs-number">8</span>, <span class="hljs-number">24</span>), cv2.FONT_HERSHEY_SIMPLEX, <span class="hljs-number">0.7</span>, (<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>), <span class="hljs-number">2</span>, cv2.LINE_AA)
        cv2.putText(frame, mode, (<span class="hljs-number">8</span>, <span class="hljs-number">24</span>), cv2.FONT_HERSHEY_SIMPLEX, <span class="hljs-number">0.7</span>, (<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>), <span class="hljs-number">1</span>, cv2.LINE_AA)
        vw.write(frame)

    vw.release()

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">write_labels</span>(<span class="hljs-params">labels, out_dir</span>):</span>
    <span class="hljs-keyword">with</span> open(os.path.join(out_dir, <span class="hljs-string">"labels.txt"</span>), <span class="hljs-string">"w"</span>, encoding=<span class="hljs-string">"utf-8"</span>) <span class="hljs-keyword">as</span> f:
        <span class="hljs-keyword">for</span> c <span class="hljs-keyword">in</span> labels:
            f.write(c + <span class="hljs-string">"\n"</span>)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    ap = argparse.ArgumentParser(description=<span class="hljs-string">"Generate a tiny synthetic gesture dataset."</span>)
    ap.add_argument(<span class="hljs-string">"--out"</span>, default=<span class="hljs-string">"data"</span>, help=<span class="hljs-string">"Output directory (default: data)"</span>)
    ap.add_argument(<span class="hljs-string">"--classes"</span>, nargs=<span class="hljs-string">"+"</span>,
                    default=[<span class="hljs-string">"swipe_left"</span>, <span class="hljs-string">"swipe_right"</span>, <span class="hljs-string">"stop"</span>],
                    help=<span class="hljs-string">"Class names (default: swipe_left swipe_right stop)"</span>)
    ap.add_argument(<span class="hljs-string">"--clips"</span>, type=int, default=<span class="hljs-number">16</span>, help=<span class="hljs-string">"Clips per class (default: 16)"</span>)
    ap.add_argument(<span class="hljs-string">"--seconds"</span>, type=float, default=<span class="hljs-number">1.5</span>, help=<span class="hljs-string">"Seconds per clip (default: 1.5)"</span>)
    ap.add_argument(<span class="hljs-string">"--fps"</span>, type=int, default=<span class="hljs-number">16</span>, help=<span class="hljs-string">"Frames per second (default: 16)"</span>)
    ap.add_argument(<span class="hljs-string">"--size"</span>, type=int, default=<span class="hljs-number">224</span>, help=<span class="hljs-string">"Frame size WxH (default: 224)"</span>)
    ap.add_argument(<span class="hljs-string">"--box"</span>, type=int, default=<span class="hljs-number">60</span>, help=<span class="hljs-string">"Box size (default: 60)"</span>)
    ap.add_argument(<span class="hljs-string">"--codec"</span>, default=<span class="hljs-string">"mp4v"</span>, help=<span class="hljs-string">"Codec fourcc (mp4v or XVID)"</span>)
    ap.add_argument(<span class="hljs-string">"--ext"</span>, default=<span class="hljs-string">".mp4"</span>, help=<span class="hljs-string">"File extension (.mp4 or .avi)"</span>)
    args = ap.parse_args()

    ensure_dir(args.out)
    write_labels(args.classes, <span class="hljs-string">"."</span>)  <span class="hljs-comment"># writes labels.txt to project root</span>

    print(<span class="hljs-string">f"Generating synthetic dataset -&gt; <span class="hljs-subst">{args.out}</span>"</span>)
    <span class="hljs-keyword">for</span> cls <span class="hljs-keyword">in</span> args.classes:
        cls_dir = os.path.join(args.out, cls)
        ensure_dir(cls_dir)
        mode = <span class="hljs-string">"stop"</span> <span class="hljs-keyword">if</span> cls == <span class="hljs-string">"stop"</span> <span class="hljs-keyword">else</span> (<span class="hljs-string">"swipe_left"</span> <span class="hljs-keyword">if</span> <span class="hljs-string">"left"</span> <span class="hljs-keyword">in</span> cls <span class="hljs-keyword">else</span> (<span class="hljs-string">"swipe_right"</span> <span class="hljs-keyword">if</span> <span class="hljs-string">"right"</span> <span class="hljs-keyword">in</span> cls <span class="hljs-keyword">else</span> <span class="hljs-string">"stop"</span>))
        <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(args.clips):
            filename = os.path.join(cls_dir, <span class="hljs-string">f"<span class="hljs-subst">{cls}</span>_<span class="hljs-subst">{i+<span class="hljs-number">1</span>:<span class="hljs-number">03</span>d}</span><span class="hljs-subst">{args.ext}</span>"</span>)
            make_clip(
                mode=mode,
                out_path=filename,
                seconds=args.seconds,
                fps=args.fps,
                size=args.size,
                box_size=args.box,
                seed=i + <span class="hljs-number">1</span>,
                codec=args.codec
            )
        print(<span class="hljs-string">f"  <span class="hljs-subst">{cls}</span>: <span class="hljs-subst">{args.clips}</span> clips"</span>)

    print(<span class="hljs-string">"Done. You can now run: python train.py, python export_onnx.py, python app.py"</span>)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    main()
</code></pre>
<p>The script generates a synthetic gesture dataset by creating video clips of a moving or stationary coloured box, simulating gestures like "swipe left," "swipe right," and "stop," and saves them in a specified output directory.</p>
<p>Now run it inside your virtual environment:</p>
<pre><code class="lang-bash">python generate_synthetic_gestures.py --out data --clips 16 --seconds 1.5
</code></pre>
<p>The command above runs a Python script named <code>generate_synthetic_gestures.py</code>, which generates a synthetic gesture dataset with 16 clips per gesture, each lasting 1.5 seconds, and saves the output in a directory named "data".</p>
<p>This creates a dataset like:</p>
<pre><code class="lang-plaintext">data/
  swipe_left/*.mp4
  swipe_right/*.mp4
  stop/*.mp4
labels.txt
</code></pre>
<p>Each folder contains short clips of a moving (or still) box that simulate gestures. This is perfect for testing the pipeline.</p>
<h3 id="heading-training-script-trainpy">Training Script: <code>train.py</code></h3>
<p>Now that we have our dataset, let’s fine-tune a Vision Transformer with temporal pooling. This model applies ViT frame-by-frame, averages embeddings across time, and trains a classification head on your gestures.</p>
<p>Here’s the full training script:</p>
<pre><code class="lang-python"><span class="hljs-comment"># train.py</span>
<span class="hljs-keyword">import</span> torch, torch.nn <span class="hljs-keyword">as</span> nn, torch.optim <span class="hljs-keyword">as</span> optim
<span class="hljs-keyword">from</span> torch.utils.data <span class="hljs-keyword">import</span> DataLoader
<span class="hljs-keyword">import</span> timm
<span class="hljs-keyword">from</span> dataset <span class="hljs-keyword">import</span> GestureClips, read_labels

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ViTTemporal</span>(<span class="hljs-params">nn.Module</span>):</span>
    <span class="hljs-string">"""Frame-wise ViT encoder -&gt; mean pool over time -&gt; linear head."""</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, num_classes, vit_name=<span class="hljs-string">"vit_tiny_patch16_224"</span></span>):</span>
        super().__init__()
        self.vit = timm.create_model(vit_name, pretrained=<span class="hljs-literal">True</span>, num_classes=<span class="hljs-number">0</span>, global_pool=<span class="hljs-string">"avg"</span>)
        feat_dim = self.vit.num_features
        self.head = nn.Linear(feat_dim, num_classes)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">forward</span>(<span class="hljs-params">self, x</span>):</span>  <span class="hljs-comment"># x: (B,T,C,H,W)</span>
        B, T, C, H, W = x.shape
        x = x.view(B * T, C, H, W)
        feats = self.vit(x)                  <span class="hljs-comment"># (B*T, D)</span>
        feats = feats.view(B, T, <span class="hljs-number">-1</span>).mean(dim=<span class="hljs-number">1</span>)  <span class="hljs-comment"># (B, D)</span>
        <span class="hljs-keyword">return</span> self.head(feats)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">train</span>():</span>
    device = <span class="hljs-string">"cuda"</span> <span class="hljs-keyword">if</span> torch.cuda.is_available() <span class="hljs-keyword">else</span> <span class="hljs-string">"cpu"</span>
    labels, _ = read_labels(<span class="hljs-string">"labels.txt"</span>)
    n_classes = len(labels)

    train_ds = GestureClips(train=<span class="hljs-literal">True</span>)
    val_ds   = GestureClips(train=<span class="hljs-literal">False</span>)
    print(<span class="hljs-string">f"Train clips: <span class="hljs-subst">{len(train_ds)}</span> | Val clips: <span class="hljs-subst">{len(val_ds)}</span>"</span>)

    <span class="hljs-comment"># Windows/CPU friendly</span>
    train_dl = DataLoader(train_ds, batch_size=<span class="hljs-number">2</span>, shuffle=<span class="hljs-literal">True</span>,  num_workers=<span class="hljs-number">0</span>, pin_memory=<span class="hljs-literal">False</span>)
    val_dl   = DataLoader(val_ds,   batch_size=<span class="hljs-number">2</span>, shuffle=<span class="hljs-literal">False</span>, num_workers=<span class="hljs-number">0</span>, pin_memory=<span class="hljs-literal">False</span>)

    model = ViTTemporal(num_classes=n_classes).to(device)
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.AdamW(model.parameters(), lr=<span class="hljs-number">3e-4</span>, weight_decay=<span class="hljs-number">0.05</span>)

    best_acc = <span class="hljs-number">0.0</span>
    epochs = <span class="hljs-number">5</span>
    <span class="hljs-keyword">for</span> epoch <span class="hljs-keyword">in</span> range(<span class="hljs-number">1</span>, epochs + <span class="hljs-number">1</span>):
        <span class="hljs-comment"># ---- Train ----</span>
        model.train()
        total, correct, loss_sum = <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.0</span>
        <span class="hljs-keyword">for</span> x, y <span class="hljs-keyword">in</span> train_dl:
            x, y = x.to(device), y.to(device)
            optimizer.zero_grad()
            logits = model(x)
            loss = criterion(logits, y)
            loss.backward()
            optimizer.step()

            loss_sum += loss.item() * x.size(<span class="hljs-number">0</span>)
            correct += (logits.argmax(<span class="hljs-number">1</span>) == y).sum().item()
            total += x.size(<span class="hljs-number">0</span>)

        train_acc = correct / total <span class="hljs-keyword">if</span> total <span class="hljs-keyword">else</span> <span class="hljs-number">0.0</span>
        train_loss = loss_sum / total <span class="hljs-keyword">if</span> total <span class="hljs-keyword">else</span> <span class="hljs-number">0.0</span>

        <span class="hljs-comment"># ---- Validate ----</span>
        model.eval()
        vtotal, vcorrect = <span class="hljs-number">0</span>, <span class="hljs-number">0</span>
        <span class="hljs-keyword">with</span> torch.no_grad():
            <span class="hljs-keyword">for</span> x, y <span class="hljs-keyword">in</span> val_dl:
                x, y = x.to(device), y.to(device)
                vcorrect += (model(x).argmax(<span class="hljs-number">1</span>) == y).sum().item()
                vtotal += x.size(<span class="hljs-number">0</span>)
        val_acc = vcorrect / vtotal <span class="hljs-keyword">if</span> vtotal <span class="hljs-keyword">else</span> <span class="hljs-number">0.0</span>

        print(<span class="hljs-string">f"Epoch <span class="hljs-subst">{epoch:<span class="hljs-number">02</span>d}</span> | train_loss <span class="hljs-subst">{train_loss:<span class="hljs-number">.4</span>f}</span> "</span>
              <span class="hljs-string">f"| train_acc <span class="hljs-subst">{train_acc:<span class="hljs-number">.3</span>f}</span> | val_acc <span class="hljs-subst">{val_acc:<span class="hljs-number">.3</span>f}</span>"</span>)

        <span class="hljs-keyword">if</span> val_acc &gt; best_acc:
            best_acc = val_acc
            torch.save(model.state_dict(), <span class="hljs-string">"vit_temporal_best.pt"</span>)

    print(<span class="hljs-string">"Best val acc:"</span>, best_acc)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    train()
</code></pre>
<p>Running the command <code>python train.py</code> initiates the training process for your gesture recognition model. Here's a breakdown of what happens:</p>
<ol>
<li><p><strong>Load your dataset from data/</strong>: The script will access and load the gesture dataset stored in the "data" directory. This dataset is used to train the model.</p>
</li>
<li><p><strong>Fine-tune a pre-trained Vision Transformer</strong>: The training script will take a Vision Transformer model that has been pre-trained on a larger dataset and fine-tune it using your specific gesture dataset. Fine-tuning helps the model adapt to the nuances of your data, improving its performance on the specific task of gesture recognition.</p>
</li>
<li><p><strong>Save the best checkpoint as vit_temporal_best.pt</strong>: During training, the script will evaluate the model's performance on a validation set. The best-performing version of the model (based on some metric like accuracy) will be saved as a checkpoint file named "vit_temporal_best.pt". This file can later be used for inference or further training.</p>
</li>
</ol>
<h4 id="heading-what-training-looks-like">What Training Looks Like</h4>
<p>You should see logs similar to this:</p>
<pre><code class="lang-plaintext">Train clips: 38 | Val clips: 10
Epoch 01 | train_loss 1.4508 | train_acc 0.395 | val_acc 0.200
Epoch 02 | train_loss 1.2466 | train_acc 0.263 | val_acc 0.200
Epoch 03 | train_loss 1.1361 | train_acc 0.368 | val_acc 0.200
Best val acc: 0.200
</code></pre>
<p>Don’t worry if your accuracy is low at first, as with the synthetic dataset that’s normal. The key is proving that the Transformer pipeline works. You can boost results later by:</p>
<ul>
<li><p>Adding more clips per class</p>
</li>
<li><p>Training for more epochs</p>
</li>
<li><p>Switching to real recorded gestures</p>
</li>
</ul>
<p><img src="https://github.com/tayo4christ/transformer-gesture/blob/07c7071bdb17bc08585baeb60d787eadc3936ef5/images/training-logs.png?raw=true" alt="Training logs" width="600" height="400" loading="lazy"></p>
<p>Figure 1. Example training logs from <code>train.py</code>, where the Vision Transformer with temporal pooling is fine-tuned on a tiny synthetic dataset.</p>
<h3 id="heading-export-the-model-to-onnx">Export the Model to ONNX</h3>
<p>To make our model easier to run in real time (and lighter on CPU), we’ll export it to the ONNX format.</p>
<p><strong>Note:</strong> ONNX, which stands for Open Neural Network Exchange, is an open-source format designed to facilitate the interchange of deep learning models between different frameworks. It lets you train a model in one framework, such as PyTorch or TensorFlow, and then deploy it in another, like Caffe2 or MXNet, without needing to completely rewrite the model. This interoperability is achieved by providing a standardized representation of the model's architecture and parameters.</p>
<p>ONNX supports a wide range of operators and is continually updated to include new features, making it a versatile choice for deploying machine learning models across various platforms and devices.</p>
<p>Create a file called <code>export_onnx.py</code>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> torch
<span class="hljs-keyword">from</span> train <span class="hljs-keyword">import</span> ViTTemporal
<span class="hljs-keyword">from</span> dataset <span class="hljs-keyword">import</span> read_labels

labels, _ = read_labels(<span class="hljs-string">"labels.txt"</span>)
n_classes = len(labels)

<span class="hljs-comment"># Load trained model</span>
model = ViTTemporal(num_classes=n_classes)
model.load_state_dict(torch.load(<span class="hljs-string">"vit_temporal_best.pt"</span>, map_location=<span class="hljs-string">"cpu"</span>))
model.eval()

<span class="hljs-comment"># Dummy input: batch=1, 16 frames, 3x224x224</span>
dummy = torch.randn(<span class="hljs-number">1</span>, <span class="hljs-number">16</span>, <span class="hljs-number">3</span>, <span class="hljs-number">224</span>, <span class="hljs-number">224</span>)

<span class="hljs-comment"># Export</span>
torch.onnx.export(
    model, dummy, <span class="hljs-string">"vit_temporal.onnx"</span>,
    input_names=[<span class="hljs-string">"video"</span>], output_names=[<span class="hljs-string">"logits"</span>],
    dynamic_axes={<span class="hljs-string">"video"</span>: {<span class="hljs-number">0</span>: <span class="hljs-string">"batch"</span>}},
    opset_version=<span class="hljs-number">13</span>
)

print(<span class="hljs-string">"Exported vit_temporal.onnx"</span>)
</code></pre>
<p>Run it with <code>python export_onnx.py</code>.</p>
<p>This generates a file <code>vit_temporal.onnx</code> in your project folder. ONNX lets us use onnxruntime, which is much faster for inference.</p>
<p>Create a file called <code>app.py</code>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os, tempfile, cv2, torch, onnxruntime, numpy <span class="hljs-keyword">as</span> np
<span class="hljs-keyword">import</span> gradio <span class="hljs-keyword">as</span> gr
<span class="hljs-keyword">from</span> dataset <span class="hljs-keyword">import</span> read_labels

T = <span class="hljs-number">16</span>
SIZE = <span class="hljs-number">224</span>
MODEL_PATH = <span class="hljs-string">"vit_temporal.onnx"</span>

labels, _ = read_labels(<span class="hljs-string">"labels.txt"</span>)

<span class="hljs-comment"># --- ONNX session + auto-detect names ---</span>
ort_session = onnxruntime.InferenceSession(MODEL_PATH, providers=[<span class="hljs-string">"CPUExecutionProvider"</span>])
<span class="hljs-comment"># detect first input and first output names to avoid mismatches</span>
INPUT_NAME = ort_session.get_inputs()[<span class="hljs-number">0</span>].name   <span class="hljs-comment"># e.g. "input" or "video"</span>
OUTPUT_NAME = ort_session.get_outputs()[<span class="hljs-number">0</span>].name <span class="hljs-comment"># e.g. "logits" or something else</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">preprocess_clip</span>(<span class="hljs-params">frames_rgb</span>):</span>
    <span class="hljs-keyword">if</span> len(frames_rgb) == <span class="hljs-number">0</span>:
        frames_rgb = [np.zeros((SIZE, SIZE, <span class="hljs-number">3</span>), dtype=np.uint8)]
    <span class="hljs-keyword">if</span> len(frames_rgb) &lt; T:
        frames_rgb = frames_rgb + [frames_rgb[<span class="hljs-number">-1</span>]] * (T - len(frames_rgb))
    frames_rgb = frames_rgb[:T]
    clip = [cv2.resize(f, (SIZE, SIZE), interpolation=cv2.INTER_AREA) <span class="hljs-keyword">for</span> f <span class="hljs-keyword">in</span> frames_rgb]
    clip = np.stack(clip, axis=<span class="hljs-number">0</span>)                                    <span class="hljs-comment"># (T,H,W,3)</span>
    clip = np.transpose(clip, (<span class="hljs-number">0</span>, <span class="hljs-number">3</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>)).astype(np.float32) / <span class="hljs-number">255</span> <span class="hljs-comment"># (T,3,H,W)</span>
    clip = (clip - <span class="hljs-number">0.5</span>) / <span class="hljs-number">0.5</span>
    clip = np.expand_dims(clip, <span class="hljs-number">0</span>)                                   <span class="hljs-comment"># (1,T,3,H,W)</span>
    <span class="hljs-keyword">return</span> clip

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_extract_path_from_gradio_video</span>(<span class="hljs-params">inp</span>):</span>
    <span class="hljs-keyword">if</span> isinstance(inp, str) <span class="hljs-keyword">and</span> os.path.exists(inp):
        <span class="hljs-keyword">return</span> inp
    <span class="hljs-keyword">if</span> isinstance(inp, dict):
        <span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> (<span class="hljs-string">"video"</span>, <span class="hljs-string">"name"</span>, <span class="hljs-string">"path"</span>, <span class="hljs-string">"filepath"</span>):
            v = inp.get(key)
            <span class="hljs-keyword">if</span> isinstance(v, str) <span class="hljs-keyword">and</span> os.path.exists(v):
                <span class="hljs-keyword">return</span> v
        <span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> (<span class="hljs-string">"data"</span>, <span class="hljs-string">"video"</span>):
            v = inp.get(key)
            <span class="hljs-keyword">if</span> isinstance(v, (bytes, bytearray)):
                tmp = tempfile.NamedTemporaryFile(delete=<span class="hljs-literal">False</span>, suffix=<span class="hljs-string">".mp4"</span>)
                tmp.write(v); tmp.flush(); tmp.close()
                <span class="hljs-keyword">return</span> tmp.name
    <span class="hljs-keyword">if</span> isinstance(inp, (list, tuple)) <span class="hljs-keyword">and</span> inp <span class="hljs-keyword">and</span> isinstance(inp[<span class="hljs-number">0</span>], str) <span class="hljs-keyword">and</span> os.path.exists(inp[<span class="hljs-number">0</span>]):
        <span class="hljs-keyword">return</span> inp[<span class="hljs-number">0</span>]
    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_read_uniform_frames</span>(<span class="hljs-params">video_path</span>):</span>
    cap = cv2.VideoCapture(video_path)
    frames = []
    total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) <span class="hljs-keyword">or</span> <span class="hljs-number">1</span>
    idxs = np.linspace(<span class="hljs-number">0</span>, total - <span class="hljs-number">1</span>, max(T, <span class="hljs-number">1</span>)).astype(int)
    want = set(int(i) <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> idxs.tolist())
    j = <span class="hljs-number">0</span>
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        ok, bgr = cap.read()
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> ok: <span class="hljs-keyword">break</span>
        <span class="hljs-keyword">if</span> j <span class="hljs-keyword">in</span> want:
            rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
            frames.append(rgb)
        j += <span class="hljs-number">1</span>
    cap.release()
    <span class="hljs-keyword">return</span> frames

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">predict_from_video</span>(<span class="hljs-params">gradio_video</span>):</span>
    video_path = _extract_path_from_gradio_video(gradio_video)
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> video_path <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> os.path.exists(video_path):
        <span class="hljs-keyword">return</span> {}
    frames = _read_uniform_frames(video_path)

    <span class="hljs-comment"># If OpenCV choked on the codec (common with recorded webm), re-encode once:</span>
    <span class="hljs-keyword">if</span> len(frames) == <span class="hljs-number">0</span>:
        tmp = tempfile.NamedTemporaryFile(delete=<span class="hljs-literal">False</span>, suffix=<span class="hljs-string">".mp4"</span>); tmp_name = tmp.name; tmp.close()
        cap = cv2.VideoCapture(video_path)
        fourcc = cv2.VideoWriter_fourcc(*<span class="hljs-string">"mp4v"</span>)
        w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) <span class="hljs-keyword">or</span> <span class="hljs-number">640</span>
        h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) <span class="hljs-keyword">or</span> <span class="hljs-number">480</span>
        out = cv2.VideoWriter(tmp_name, fourcc, <span class="hljs-number">20.0</span>, (w, h))
        <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
            ok, frame = cap.read()
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> ok: <span class="hljs-keyword">break</span>
            out.write(frame)
        cap.release(); out.release()
        frames = _read_uniform_frames(tmp_name)

    clip = preprocess_clip(frames)
    <span class="hljs-comment"># &gt;&gt;&gt; use the detected ONNX input/output names &lt;&lt;&lt;</span>
    logits = ort_session.run([OUTPUT_NAME], {INPUT_NAME: clip})[<span class="hljs-number">0</span>]  <span class="hljs-comment"># (1, C)</span>
    probs = torch.softmax(torch.from_numpy(logits), dim=<span class="hljs-number">1</span>)[<span class="hljs-number">0</span>].numpy().tolist()
    <span class="hljs-keyword">return</span> {labels[i]: float(probs[i]) <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(labels))}

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">predict_from_image</span>(<span class="hljs-params">image</span>):</span>
    <span class="hljs-keyword">if</span> image <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
        <span class="hljs-keyword">return</span> {}
    clip = preprocess_clip([image] * T)
    logits = ort_session.run([OUTPUT_NAME], {INPUT_NAME: clip})[<span class="hljs-number">0</span>]
    probs = torch.softmax(torch.from_numpy(logits), dim=<span class="hljs-number">1</span>)[<span class="hljs-number">0</span>].numpy().tolist()
    <span class="hljs-keyword">return</span> {labels[i]: float(probs[i]) <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(len(labels))}

<span class="hljs-keyword">with</span> gr.Blocks() <span class="hljs-keyword">as</span> demo:
    gr.Markdown(<span class="hljs-string">"# Gesture Classifier (ONNX)\nRecord or upload a short video, then click **Classify Video**."</span>)
    <span class="hljs-keyword">with</span> gr.Tab(<span class="hljs-string">"Video (record or upload)"</span>):
        vid_in = gr.Video(label=<span class="hljs-string">"Record from webcam or upload a short clip"</span>)
        vid_out = gr.Label(num_top_classes=<span class="hljs-number">3</span>, label=<span class="hljs-string">"Prediction"</span>)
        gr.Button(<span class="hljs-string">"Classify Video"</span>).click(fn=predict_from_video, inputs=vid_in, outputs=vid_out)
    <span class="hljs-keyword">with</span> gr.Tab(<span class="hljs-string">"Single Image (fallback)"</span>):
        img_in = gr.Image(label=<span class="hljs-string">"Upload an image frame"</span>, type=<span class="hljs-string">"numpy"</span>)
        img_out = gr.Label(num_top_classes=<span class="hljs-number">3</span>, label=<span class="hljs-string">"Prediction"</span>)
        gr.Button(<span class="hljs-string">"Classify Image"</span>).click(fn=predict_from_image, inputs=img_in, outputs=img_out)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    demo.launch()
</code></pre>
<p>Running the command <code>python app.py</code> launches a Gradio application in your web browser. Here's what happens:</p>
<ol>
<li><p><strong>Webcam feed streams live</strong>: The application accesses your webcam to provide a live video feed. This allows you to perform gestures in front of the camera in real-time.</p>
</li>
<li><p><strong>Predictions update continuously</strong>: As you perform gestures, the model processes the video frames continuously, updating its predictions in real-time.</p>
</li>
<li><p><strong>Top 3 gesture classes displayed with probabilities</strong>: The application displays the top three predicted gesture classes along with their probabilities, giving you an idea of the model's confidence in its predictions.</p>
</li>
</ol>
<p>When you open the app in your browser, you'll find two tabs. In the <strong>Video tab</strong>, you can click <em>Record from webcam</em> to capture a short clip of your gesture, typically lasting 2–4 seconds. After recording, click <strong>Classify Video</strong>. The model will then process the captured frames using the Transformer model and display the predicted gesture probabilities. This setup allows for interactive testing and demonstration of the gesture recognition system.</p>
<p>Here’s an example where I raised my hand for a <strong>stop</strong> gesture, and the model predicts “stop” as the top class:</p>
<p><img src="https://github.com/tayo4christ/transformer-gesture/blob/07c7071bdb17bc08585baeb60d787eadc3936ef5/images/realtime-demo.png?raw=true" alt="Gradio demo output" width="600" height="400" loading="lazy"></p>
<p>Figure 2. The Gradio app running locally. After recording a short clip, the Transformer model predicts the gesture with class probabilities.</p>
<h3 id="heading-evaluate-accuracy-latency">Evaluate Accuracy + Latency</h3>
<p>Now that the model runs in a demo app, let’s check how well it performs. There are two sides to this:</p>
<ul>
<li><p><strong>Accuracy</strong>: does the model predict the right gesture class?</p>
</li>
<li><p><strong>Latency</strong>: how fast does it respond, especially on CPU vs GPU?</p>
</li>
</ul>
<h4 id="heading-1-quick-accuracy-check">1. Quick Accuracy Check</h4>
<p>Save this as <code>eval.py</code> in the same folder as your other scripts:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> torch
<span class="hljs-keyword">from</span> dataset <span class="hljs-keyword">import</span> GestureClips, read_labels
<span class="hljs-keyword">from</span> train <span class="hljs-keyword">import</span> ViTTemporal

labels, _ = read_labels(<span class="hljs-string">"labels.txt"</span>)
n_classes = len(labels)

<span class="hljs-comment"># Load validation data</span>
val_ds = GestureClips(train=<span class="hljs-literal">False</span>)
val_dl = torch.utils.data.DataLoader(val_ds, batch_size=<span class="hljs-number">2</span>, shuffle=<span class="hljs-literal">False</span>)

<span class="hljs-comment"># Load trained model</span>
model = ViTTemporal(num_classes=n_classes)
model.load_state_dict(torch.load(<span class="hljs-string">"vit_temporal_best.pt"</span>, map_location=<span class="hljs-string">"cpu"</span>))
model.eval()

correct, total = <span class="hljs-number">0</span>, <span class="hljs-number">0</span>
all_preds, all_labels = [], []

<span class="hljs-keyword">with</span> torch.no_grad():
    <span class="hljs-keyword">for</span> x, y <span class="hljs-keyword">in</span> val_dl:
        logits = model(x)
        preds = logits.argmax(dim=<span class="hljs-number">1</span>)
        correct += (preds == y).sum().item()
        total += y.size(<span class="hljs-number">0</span>)
        all_preds.extend(preds.tolist())
        all_labels.extend(y.tolist())

print(<span class="hljs-string">f"Validation accuracy: <span class="hljs-subst">{correct/total:<span class="hljs-number">.2</span>%}</span>"</span>)
</code></pre>
<h4 id="heading-2-confusion-matrix">2. Confusion Matrix</h4>
<p>Let’s also visualize which gestures are confused. Add this snippet at the bottom of <code>eval.py</code>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> matplotlib.pyplot <span class="hljs-keyword">as</span> plt
<span class="hljs-keyword">import</span> seaborn <span class="hljs-keyword">as</span> sns
<span class="hljs-keyword">from</span> sklearn.metrics <span class="hljs-keyword">import</span> confusion_matrix

cm = confusion_matrix(all_labels, all_preds)

plt.figure(figsize=(<span class="hljs-number">6</span>,<span class="hljs-number">6</span>))
sns.heatmap(cm, annot=<span class="hljs-literal">True</span>, fmt=<span class="hljs-string">"d"</span>, xticklabels=labels, yticklabels=labels, cmap=<span class="hljs-string">"Blues"</span>)
plt.xlabel(<span class="hljs-string">"Predicted"</span>)
plt.ylabel(<span class="hljs-string">"True"</span>)
plt.title(<span class="hljs-string">"Confusion Matrix"</span>)
plt.tight_layout()
plt.show()
</code></pre>
<p>When you run <code>python eval.py</code>, a heatmap like this will pop up:</p>
<p><img src="https://github.com/tayo4christ/transformer-gesture/blob/07c7071bdb17bc08585baeb60d787eadc3936ef5/images/confusion-matrix.png?raw=true" alt="Confusion matrix" width="600" height="400" loading="lazy"></p>
<p>Figure 3. Confusion matrix on the validation set. Correct predictions appear along the diagonal. Off-diagonal counts show gesture confusions.</p>
<h4 id="heading-3-latency-benchmark">3. Latency Benchmark</h4>
<p>Finally, let’s see how fast inference runs. Save the following as <code>benchmark.py</code>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> time, numpy <span class="hljs-keyword">as</span> np, onnxruntime
<span class="hljs-keyword">from</span> dataset <span class="hljs-keyword">import</span> read_labels

labels, _ = read_labels(<span class="hljs-string">"labels.txt"</span>)

ort = onnxruntime.InferenceSession(<span class="hljs-string">"vit_temporal.onnx"</span>, providers=[<span class="hljs-string">"CPUExecutionProvider"</span>])
INPUT_NAME = ort.get_inputs()[<span class="hljs-number">0</span>].name
OUTPUT_NAME = ort.get_outputs()[<span class="hljs-number">0</span>].name

dummy = np.random.randn(<span class="hljs-number">1</span>, <span class="hljs-number">16</span>, <span class="hljs-number">3</span>, <span class="hljs-number">224</span>, <span class="hljs-number">224</span>).astype(np.float32)

<span class="hljs-comment"># Warmup</span>
<span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(<span class="hljs-number">3</span>):
    ort.run([OUTPUT_NAME], {INPUT_NAME: dummy})

<span class="hljs-comment"># Benchmark</span>
t0 = time.time()
<span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(<span class="hljs-number">50</span>):
    ort.run([OUTPUT_NAME], {INPUT_NAME: dummy})
t1 = time.time()

print(<span class="hljs-string">f"Average latency: <span class="hljs-subst">{(t1 - t0)/<span class="hljs-number">50</span>:<span class="hljs-number">.3</span>f}</span> seconds per clip"</span>)
</code></pre>
<p>Run: <code>python benchmark.py</code></p>
<p>On CPU, you might see ~0.05–0.15s per clip; on GPU it’s much faster.</p>
<p><strong>Note</strong>: If latency is high, you can enable <strong>quantization</strong> in ONNX to shrink the model and speed up inference.</p>
<h2 id="heading-option-2-use-small-samples-from-public-gesture-datasets">Option 2: Use Small Samples from Public Gesture Datasets</h2>
<p>If you’d prefer to see your model trained on <em>real</em> gesture clips instead of synthetic moving boxes, you can grab a handful of videos from open datasets. You don’t need to download the entire dataset (which can be several GB) just a few <code>.mp4</code> samples are enough to follow along.</p>
<h3 id="heading-recommended-sources">Recommended sources</h3>
<ul>
<li><p><strong>20BN Jester Dataset</strong>: Contains short clips of hand gestures like swiping, clapping, and pointing.</p>
</li>
<li><p><strong>WLASL</strong>: A large-scale dataset of isolated sign language words.</p>
</li>
</ul>
<p>Both projects provide small <code>.mp4</code> videos you can use as realistic training examples. I’ve linked them below.</p>
<h3 id="heading-setting-up-your-dataset-folder">Setting up your dataset folder</h3>
<p>Once you download a few clips, place them in the <code>data/</code> folder under subfolders named after each gesture class. For example:</p>
<pre><code class="lang-plaintext">data/
├── swipe_left/
│   ├── clip1.mp4
│   └── clip2.mp4
├── swipe_right/
│   ├── clip1.mp4
│   └── clip2.mp4
└── stop/
    ├── clip1.mp4
    └── clip2.mp4
</code></pre>
<p>And update <code>labels.txt</code> to match the folder names:</p>
<pre><code class="lang-plaintext">swipe_left
swipe_right
stop
</code></pre>
<p>Now your dataset is ready, and the same training scripts from earlier (<code>train.py</code>, <code>eval.py</code>) will work without modification.</p>
<h3 id="heading-why-choose-this-option">Why choose this option?</h3>
<ul>
<li><p>Gives more realistic results than synthetic coloured boxes</p>
</li>
<li><p>Lets you see how the model handles <em>actual human hand movements</em></p>
</li>
<li><p>It just requires a bit more effort (downloading clips, trimming them if needed)</p>
</li>
</ul>
<p><strong>Tip:</strong> If downloading from these datasets feels too heavy, you can also record your own short gestures using your laptop webcam. Just save them as <code>.mp4</code> files and organize them in the same folder structure.</p>
<h2 id="heading-accessibility-notes-amp-ethical-limits">Accessibility Notes &amp; Ethical Limits</h2>
<p>While this project shows the technical workflow for gesture recognition with Transformers, it’s important to step back and consider the <strong>human context</strong>:</p>
<ul>
<li><p><strong>Accessibility first</strong>: Tools like this can help students with speech or motor difficulties, but they should always be co-designed with the people who will use them. Don’t assume one-size-fits-all.</p>
</li>
<li><p><strong>Dataset sensitivity</strong>: Using publicly available sign or gesture datasets is fine for prototyping, but deploying such a system requires careful consideration of consent and representation.</p>
</li>
<li><p><strong>Error tolerance</strong>: Even small misclassifications can have big consequences in accessibility contexts (for example, confusing <em>stop</em> with <em>go</em>). Always plan for fallback options (like manual input or confirmation).</p>
</li>
<li><p><strong>Bias and inclusivity</strong>: Models trained on narrow datasets may fail for different skin tones, lighting conditions, or cultural gesture variations. Broad and diverse training data is essential for fairness.</p>
</li>
</ul>
<p>In other words: this demo is a <strong>teaching scaffold</strong>, not a production-ready accessibility tool. Responsible deployment requires collaboration with educators, therapists, and end users.</p>
<h2 id="heading-next-steps">Next Steps</h2>
<p>If you’d like to push this project further, here are some directions to explore:</p>
<ul>
<li><p><strong>Better models</strong>: Try video-focused Transformers like <a target="_blank" href="https://arxiv.org/abs/2102.05095">TimeSformer</a> or <a target="_blank" href="https://arxiv.org/abs/2203.12602">VideoMAE</a> for stronger temporal reasoning.</p>
</li>
<li><p><strong>Larger vocabularies</strong>: Add more gesture classes, build your own dataset, or use portions of public datasets like <a target="_blank" href="https://www.kaggle.com/datasets/toxicmender/20bn-jester">20BN Jester</a> or <a target="_blank" href="https://www.kaggle.com/datasets/risangbaskoro/wlasl-processed">WLASL.</a></p>
</li>
<li><p><strong>Pose fusion</strong>: Combine gesture video with human pose keypoints from <a target="_blank" href="https://mediapipe.readthedocs.io/en/latest/solutions/hands.html">MediaPipe</a> or <a target="_blank" href="https://github.com/CMU-Perceptual-Computing-Lab/openpose">OpenPose</a> for more robust predictions.</p>
</li>
<li><p><strong>Real-time smoothing</strong>: Implement temporal smoothing or debounce logic in the app so predictions are more stable during live use.</p>
</li>
<li><p><strong>Quantization + edge devices</strong>: Convert your ONNX model to an INT8 quantized version and deploy it on a Raspberry Pi or Jetson Nano for classroom-ready prototypes.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you learned how to create a gesture recognition system using Transformer models, demonstrating the potential of cutting-edge machine learning techniques. By preparing a small dataset, training a Vision Transformer with temporal pooling, exporting the model to ONNX for efficient inference, and deploying a real-time Gradio app, you showcased a practical application of these technologies. The evaluation of accuracy and latency further highlighted the system's effectiveness and responsiveness.</p>
<p>This project illustrates how you can leverage advanced ML methods to enhance accessibility and communication, paving the way for more inclusive learning environments.</p>
<p>Remember: while this demo works with small datasets, real-world applications need larger, more diverse data and careful consideration of accessibility, inclusivity, and ethics.</p>
<p>Here’s the GitHub repo for full source code: <a target="_blank" href="https://github.com/tayo4christ/transformer-gesture">transformer-gesture</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Checkmate Patterns in Chess for Beginners ]]>
                </title>
                <description>
                    <![CDATA[ After learning the rules and basics of chess, there are various concepts that beginners should learn in order to keep improving. Some of these concepts include opening principles, middle game concepts, endgame fundamentals, and strategy. In this hand... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/checkmate-patterns-in-chess-for-beginners/</link>
                <guid isPermaLink="false">68a72ee03bbf12346853dce9</guid>
                
                    <category>
                        <![CDATA[ chess ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Strategy ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ihechikara Abba ]]>
                </dc:creator>
                <pubDate>Thu, 21 Aug 2025 14:36:16 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755720597720/2bb80910-7697-45e2-8163-dbfb73b4998f.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>After learning the rules and basics of chess, there are various concepts that beginners should learn in order to keep improving. Some of these concepts include opening principles, middle game concepts, endgame fundamentals, and strategy.</p>
<p>In this handbook, we’ll focus on endgame fundamentals. You’ll learn about some common checkmate patterns for beginners and how you can recognize these patterns more easily to increase your chances of winning.</p>
<p>This handbook is not for absolute beginners. You should understand these concepts before proceeding:</p>
<ul>
<li><p>How to identify each chess piece.</p>
</li>
<li><p>How to set up a chess board.</p>
</li>
<li><p>How each piece moves.</p>
</li>
<li><p>Some basic chess terms like "capture", "castling", "check", "checkmate", "piece promotion", "fork", "pin", and so on.</p>
</li>
</ul>
<p>If you're new to chess, or you're not yet familiar with the prerequisites, then <a target="_blank" href="https://lichess.org/learn">this free resource</a> is a good place to start.</p>
<p>In the first chapter, you'll learn about algebraic chess notations. You’ll learn about files and ranks, how to identify and denote each square on a board, and how to record chess moves using algebraic chess notations.</p>
<p>In the second chapter, you’ll learn about some checkmate patterns like queen and king vs king, two rooks vs king, opera mate, Anastasia’s mate, and so on.</p>
<p>I've provided some exercises in different sections to help you practice. You can find the answers to each exercise at the end of the handbook.</p>
<p>I’ve also included some practice positions for each endgame pattern. To use them, click on the link provided for each exercise, then click on “CONTINUE FROM HERE” in the options at the bottom-right of the page. That is:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755630305311/3fdcec2a-a6b2-485b-9e78-1a1620d522a0.jpeg" alt="press the &quot;CONTINUE FROM HERE&quot; button to practice the position against an engine" class="image--center mx-auto" width="971" height="816" loading="lazy"></p>
<p>This will let you practice the positions against an engine. The engine has varying levels of strength, so you should try beating each level. You can find links for all the practice positions at the end of the article. You can also use the board editor from the links to set up your own positions.</p>
<p>You can watch the video version of this handbook here:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/1AX7_aPWmbE" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-how-to-use-this-handbook">How to Use this Handbook</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-1-what-are-notations-in-chess-algebraic-chess-notation-explained">Chapter 1: What are Notations in Chess? Algebraic Chess Notation Explained</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-are-chess-notations">What are Chess Notations?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-algebraic-chess-notation">What is Algebraic Chess Notation?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-are-chess-notations-used-for">What are Chess Notations Used For?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-write-algebraic-chess-notations">How to Write Algebraic Chess Notations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-identify-squares-on-a-chess-board">How to Identify Squares on a Chess Board</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-chapter-2-checkmate-patterns-for-beginners">Chapter 2: Checkmate Patterns for Beginners</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-a-check-and-checkmate-in-chess">What is a Check and Checkmate in Chess?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-checkmate-patterns">Checkmate Patterns</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-checkmate-patterns-ii">Checkmate Patterns II</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-exercise-answers">Exercise Answers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-practice-position-urls">Practice Position URLs</a></p>
</li>
</ol>
<h2 id="heading-how-to-use-this-handbook"><strong>How to Use this Handbook</strong></h2>
<p>This handbook is not meant to be read and understood just once. If you're a beginner, then chances are high that you'll reference it multiple times in order to understand certain concepts.</p>
<p>In order to get the best out of this resource, you'll need a chess board. You can use either a physical board or an online chessboard. I recommend using <a target="_blank" href="https://lichess.org/">Lichess</a> if you're going to use the latter option.</p>
<p>You should try and replicate the moves as you read. Remember, you're not meant to understand everything the first time you read it, so it’s totally fine to go over one concept again and again.</p>
<p>Chess books can be boring, but don't let that hinder your progress. It gets interesting when you're able to understand, practice, and see improvements from what you've learned.</p>
<p>Let's get started!</p>
<h2 id="heading-chapter-1-what-are-notations-in-chess-algebraic-chess-notation-explained">Chapter 1: What are Notations in Chess? Algebraic Chess Notation Explained</h2>
<p>You can use chess notations to record chess moves, and this handbook will rely heavily on them to teach you difference concepts about chess as a beginner. The first time you learn about writing or reading notations may seem overwhelming, but don't worry – with constant practice and studying, you'll get the hang of it.</p>
<h3 id="heading-what-are-chess-notations"><strong>What are Chess Notations?</strong></h3>
<p>To put it simply, chess notation is a method of recording moves in a chess game. In a physical chess game, it usually involves two players writing down moves as they’re played.</p>
<p>While there are other forms of chess notations like descriptive, coordinate, numeric, and so on, we'll focus primarily on algebraic chess notation.</p>
<h3 id="heading-what-is-algebraic-chess-notation"><strong>What is Algebraic Chess Notation?</strong></h3>
<p>Algebraic chess notation is the standard way of recording chess. Each notation shows the move number, the piece moved, and the position of the board where the piece was moved to. In some cases, the notations also show an action being performed, like a check, a checkmate, castling, and so on.</p>
<p>For example:</p>
<ol>
<li><p>e4 d5</p>
</li>
<li><p>d4 dxe4</p>
</li>
<li><p>Nc3 e6</p>
</li>
</ol>
<p>These may look strange if it's your first time looking at notations. But don't worry, you'll understand how to write and read/interpret them as we progress.</p>
<p>In online games, they're recorded automatically:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278278381/25c66717-56a0-46ac-99dd-5db78c6b1a37.png" alt="chess notations on Lichess" class="image--center mx-auto" width="2510" height="1291" loading="lazy"></p>
<p>The image above shows a chess game played on <a target="_blank" href="https://lichess.org/">lichess.org</a>. On the right side of the image, you can see the notations of the game as each move was played, along with numbers corresponding with the moves. We'll talk more about the numbers later.</p>
<p>Now that you know what notations are, let's talk about why they're needed.</p>
<h3 id="heading-what-are-chess-notations-used-for"><strong>What are Chess Notations Used For?</strong></h3>
<p>Here are some of the uses of chess notations:</p>
<h4 id="heading-competitiveprofessional-chess">Competitive/Professional Chess</h4>
<p>Algebraic chess notation is the standard way of recording a chess game in tournaments. In over the board tournaments, each player is given a piece of paper with columns for writing each move played. Note that this involves recording their opponent's moves as well.</p>
<h4 id="heading-studying-and-teaching-chess">Studying and Teaching Chess</h4>
<p>You can think of chess notations as the language of chess, and to learn about something, there has to be a way to pass and communicate information.</p>
<h4 id="heading-communication">Communication</h4>
<p>With chess notations, you can communicate moves and share games with players across the world without needing a physical board.</p>
<h3 id="heading-how-to-write-algebraic-chess-notations"><strong>How to Write Algebraic Chess Notations</strong></h3>
<p>Algebraic chess notation makes use of the board coordinates and the pieces to denote moves.</p>
<p>Let's start with the coordinates:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278306647/e0c0d1d4-74cb-4f49-9180-42b81102f7d2.png" alt="an empty chess board" class="image--center mx-auto" width="1093" height="1096" loading="lazy"></p>
<p>The image above shows a chess board without the pieces. On the right edge of the board are numbers 1 to 8, and the bottom edge of the board has letters a to h. You can use these numbers and letters to refer to a particular position of the board.</p>
<p>Each vertical column (top to bottom) in a chess board is called a file, while each horizontal row (left to right) is called a rank. You can also associate them with the letters and numbers: the letters can be used to reference files, while the numbers can be used to reference ranks.</p>
<h4 id="heading-files-a-to-f">Files a to f</h4>
<p>There are eight files in a chess board. Another way to say this is that, from top to bottom, there are eight vertical lines (stack of squares from top to bottom) in a chess board. Each vertical line is associated with a letter, which is usually written at the bottom edge of the board. That is:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278341256/ee7fae5e-3a9d-4477-b036-dbcaa031b92d.png" alt="a chess board show an arrow on each file" class="image--center mx-auto" width="1098" height="1096" loading="lazy"></p>
<p>To help you understand files, let's try and identify some of them separately. The image below shows the a-file:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278367782/da2fab28-9cc3-47a8-ad90-cad1e55cfbb7.png" alt="a chess board with an arrow on the a-file" class="image--center mx-auto" width="1087" height="1087" loading="lazy"></p>
<p>We can tell that this is the a-file because the all the squares in that vertical column fall under the part of the board labelled "a". Remember that files go from top to bottom.</p>
<p>Here's another example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278386430/f2842bda-1dee-466e-9ddb-70328001873c.png" alt="a chess board with an arrow on the f-file" class="image--center mx-auto" width="1093" height="1092" loading="lazy"></p>
<p>This arrow runs from top to bottom, but on a different part of the board. To know what file this is, simply look the the letter associated with the vertical column. Using that, we can say that the arrow is on the the f-file.</p>
<p>Here's an exercise for you (you can find the answers to each exercise at the end of the handbook):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278412195/1f83d8b6-d192-4f60-9604-5a8de609ceec.png" alt="an exercise to find what file the arrow is on" class="image--center mx-auto" width="1093" height="1087" loading="lazy"></p>
<p>In the image above, using the letters on the board, what file is the arrow on?</p>
<h4 id="heading-ranks-1-to-8">Ranks 1 to 8</h4>
<p>Just like files, we have eight ranks in a chess board. These are the eight horizontal lines (stack of squares from left to right) in a chess board. That is:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278462326/7308dc99-8679-40f9-bd98-02a7644ba14a.png" alt="a chessboard with an arrow on each rank" class="image--center mx-auto" width="1089" height="1090" loading="lazy"></p>
<p>Let's identify some of the ranks separately.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278544960/1e785b64-dc0a-4770-a70c-95e9bdfd4a06.png" alt="a chessboard with an arrow on the fourth rank" class="image--center mx-auto" width="1087" height="1087" loading="lazy"></p>
<p>Using the position of the arrow, you can tell the rank. The arrow run from left to right on the part of the board with the number 4, so we can say that it is on the fourth rank.</p>
<p>Here's another example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278560474/1b75018e-2197-4d7f-8614-add51e756024.png" alt="a chessboard with an arrow on the first rank" class="image--center mx-auto" width="1087" height="1080" loading="lazy"></p>
<p>You may confuse files and ranks here since you can see the letters at the bottom. Remember, we're dealing with ranks here, and they go from left to right, not top to bottom. From left to right, in the image above, we can see the letter 1. So the arrow is on the first rank.</p>
<p>Here's an exercise for you:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278592936/77305a72-bbc8-45f9-aff4-82a9cbb8f9de.png" alt="a chessboard with an arrow on the eighth rank" class="image--center mx-auto" width="1089" height="1086" loading="lazy"></p>
<p>In the image above, using the numbers on the board, what rank is the arrow on?</p>
<h3 id="heading-how-to-identify-squares-on-a-chess-board">How to Identify Squares on a Chess Board</h3>
<p>Now that you know what files and ranks are in chess, let's see how you can use them to identify a square. Identifying a square is important when writing notations. Each notation is made up of the move number, the piece moved, and the coordinate. In the case, a coordinate is the combination of a file and a rank.</p>
<p>Let's start with this image:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278624546/a25a44db-7950-462b-ac2d-24c6105743f6.png" alt="a chessboard with a circle on e5" class="image--center mx-auto" width="1093" height="1084" loading="lazy"></p>
<p>In the image above, we have a circle on a random square. The goal here is to identify that square, that is, to refer to it as "something".</p>
<p>To identify the square, you have to trace its file and the rank. Let' start with the file.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278670477/1e0355a5-35f7-4098-adba-0ab1b7fb5980.png" alt="a chessboard with a circle and arrow pointing to e5" class="image--center mx-auto" width="1090" height="1084" loading="lazy"></p>
<p>Using the arrow, we can see that the circle falls under the e-file. Next, let's trace the rank.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278713838/05fe7d3c-0902-4b85-b7f9-270b9475146b.png" alt="a chessboard with a circle and arrows pointing to e5" class="image--center mx-auto" width="1089" height="1086" loading="lazy"></p>
<p>Great! We can tell that the circle is on the fifth rank.</p>
<p>So we can say that we have a circle on the e-file and the fifth rank. Using notations, you can write it as e5.</p>
<p>In the next example, we'll identify the position of chess pieces. In order to do that, you need to know the letters that represent each piece on a board:</p>
<p>King = K</p>
<p>Queen = Q</p>
<p>Rook = R</p>
<p>Bishop = B</p>
<p>Knight = N</p>
<p>Pawn = no letter (denoted by the square they move to).</p>
<p>In our last example, we identified the square as e5. If you replace that square with a chess piece, then the the notation would start with the letter of that piece, followed by its position on the board. Here's an example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278735253/f4b13367-7bbe-48f9-b7e3-f8359081cbe0.png" alt="queen on e5" class="image--center mx-auto" width="1087" height="1086" loading="lazy"></p>
<p>Now we have the queen on the e5 square. The notation will look like this: Qe5. This way of writing the letter of the piece, followed by the coordinate of the square applies to other pieces, except the pawns. For pawns, you only write the coordinate.</p>
<p>If you had a rook on e5, the notation would be Re5, Ke5 for a king, Ne5 for a knight, Be5 for a bishop, and e5 for a pawn.</p>
<h3 id="heading-how-to-write-algebraic-chess-notations-1"><strong>How to Write Algebraic Chess Notations</strong></h3>
<p>We stated earlier that algebraic chess notations show the move number, the piece moved, and the position of the board where the piece is moved to. In our examples, you've seen how to write the piece moved, and where it was moved to using coordinate (files and ranks). In the examples for this section, we'll add the move number to complete the algebraic chess notation.</p>
<p>By move number, we simply mean the turn count. Each move number is associated with one White and one Black move. For instance, the first move of the game will have a move number of 1. Let's look at some examples.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278762190/45bcd65e-3901-40b0-a602-494bfc696643.png" alt="chessboard starting position" class="image--center mx-auto" width="1132" height="1123" loading="lazy"></p>
<p>Each piece on the board is in its starting position. Let's make the first move for White:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278784164/52dc3f3b-16eb-47ef-b157-2d1bef6f0564.png" alt="first pawn move for white" class="image--center mx-auto" width="1129" height="1120" loading="lazy"></p>
<p>White has moved a pawn to e4. To record this, you'd start with the move number, followed by the piece and coordinate. That is:</p>
<ol>
<li>e4</li>
</ol>
<p>Next, Black's move.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278805904/2c4632a4-74cb-45df-9c4e-b7c9553c1700.png" alt="first pawn move for black" class="image--center mx-auto" width="1123" height="1122" loading="lazy"></p>
<p>Black responded by moving a pawn to e4. Since this is Black's first move, it'll also be recorded under move number 1:</p>
<ol>
<li>e4 e5</li>
</ol>
<p>If you look at the algebraic notation above, you can see that both notations are written on the same line, and are both associated with move number 1. To interpret this, the first coordinate belongs to White, while the second belongs to Black.</p>
<p>To make this even clearer, let's continue the game. It's White's turn to play.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278832714/22efd090-04bb-485d-b0be-8eada8d76784.png" alt="white knight to f3" class="image--center mx-auto" width="1125" height="1120" loading="lazy"></p>
<p>The white knight was moved to f3, which can be denoted by Nf3. Since this is White's second move, it'll be recorded under move number 2:</p>
<ol>
<li><p>e4 e5</p>
</li>
<li><p>Nf3</p>
</li>
</ol>
<p>Now Black has to respond with a second move.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278861739/d4e8e53f-6753-4ad8-ac2d-2e44c573c63c.png" alt="black knight to c6" class="image--center mx-auto" width="1119" height="1117" loading="lazy"></p>
<p>Black played Nc6 (knight to c6). Remember that this is the second move for Black, so it has to be recorded in move number 2, right after the notation for White's second move. So we'll have this:</p>
<ol>
<li><p>e4 e5</p>
</li>
<li><p>Nf3 Nc6</p>
</li>
</ol>
<p>This system of recording chess moves is known as algebraic chess notation. Here's a summary of how it works:</p>
<ul>
<li><p>You write the move number.</p>
</li>
<li><p>You write piece moved (omitted for pawns).</p>
</li>
<li><p>You write the coordinate (file and rank) that the piece moved to.</p>
</li>
</ul>
<p>With this information, you can easily read and write chess notations! But we're not done yet. How do you write checks, checkmates, castling, and pawn promotions?</p>
<h3 id="heading-how-to-write-a-check-notation-in-algebraic-chess-notations"><strong>How to Write a Check Notation in Algebraic Chess Notations</strong></h3>
<p>You can use the + symbol to denote a check. This comes after the move notation. That is, you write the notation as you would for any other move, and then add the + symbol at the end. Here's an example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278891757/ecb8de32-58c3-481c-8fa2-e229cd555fab.png" alt="queen check" class="image--center mx-auto" width="1117" height="1116" loading="lazy"></p>
<p>Here, the queen moves to e7 to check the king. So the notation would be Qe7+. The + at the end denotes the check by the queen. If you write it as Qe7, without the check symbol, then you'd have an inaccurate notation of the move.</p>
<h3 id="heading-how-to-write-a-checkmate-notation-in-algebraic-chess-notations"><strong>How to Write a Checkmate Notation in Algebraic Chess Notations</strong></h3>
<p>A checkmate is denoted by the # symbol. It also comes after the move notation. That is, you write the notation as you would for any other move, and then add the # symbol at the end. Here's an example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278908199/7a808ce3-25eb-419b-ac9f-ef03a4bcb4ef.png" alt="checkmate with two rooks" class="image--center mx-auto" width="1117" height="1116" loading="lazy"></p>
<p>In the image above, the rook moved to g8 to win the game. The notation would be Rg8#. The # symbol denotes the checkmate.</p>
<h3 id="heading-how-to-write-a-castling-notation-in-algebraic-chess-notations"><strong>How to Write a Castling Notation in Algebraic Chess Notations</strong></h3>
<p>In a chess game, castling can either be to the kingside or queenside. Kingside castling is denoted using O-O, while queenside castling is denoted using O-O-O. Note that the symbol is not a zero. Rather, it's an O, as in <strong>o</strong>range.</p>
<h3 id="heading-how-to-write-a-capture-move-in-algebraic-chess-notations"><strong>How to Write a Capture Move in Algebraic Chess Notations</strong></h3>
<p>Capture moves are denoted using the x symbol. Here are some different variations:</p>
<ul>
<li><p>Qxg6 means that the queen captured a piece on g6.</p>
</li>
<li><p>Qxg6+ means that the queen captured a piece on g6 and checked the king.</p>
</li>
<li><p>Qxg8# means that the queen captured a piece on g8 and checkmated the king.</p>
</li>
<li><p>dxe4 mean that a pawn captured a piece on e4.</p>
</li>
</ul>
<p>In a case where you have two pieces that can move to the same square, you have to include the file of the piece that was moved. Here's an example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755278931250/f8345127-37c3-4b34-aa72-67f625a7d8af.png" alt="two knights attacking a rook on g8" class="image--center mx-auto" width="1084" height="1084" loading="lazy"></p>
<p>In the board above, we have two knights: one on the f-file and another on the h-file. Both knights can capture the rook on g8, and it may be confusing for someone reading your notations if you don't specify which knight was moved.</p>
<p>To denote this, you have to add the file from where the knight was moved in your notation. So, if you capture with the knight on the f-file, you'd have Nfxg8 (knight on f-file captured a piece on g8). On the other hand, capturing with the knight on the h-file will result to a notation like this: Khxg8.</p>
<p>This is not only specific to capturing pieces. If you have two knights that can move to the same square, then you'd have to include the previous file of whichever knight that is moved. So, assuming that we didn't have a rook to capture on g8, you'd have Nfg8 to denote that the knight on f-file moved to g8, and Nhg8 to denote that the knight on the h-file moved to g8.</p>
<h3 id="heading-how-to-denote-piece-promotion-in-algebraic-chess-notations"><strong>How to Denote Piece Promotion in Algebraic Chess Notations</strong></h3>
<p>You can use the = symbol to denote the promotion of a pawn. That is:</p>
<ul>
<li><p>e8=Q means that pawn moved to e8 and got promoted to a queen.</p>
</li>
<li><p>e8=Q+ means that pawn moved to e8, got promoted to queen, and checked the king.</p>
</li>
<li><p>e8=Q# means that pawn moved to e8, got promoted to queen, and checkmated the king.</p>
</li>
<li><p>e8=N means that pawn moved to e8 and got promoted to a knight.</p>
</li>
</ul>
<p>Here are two exercises for you:</p>
<ul>
<li><p>How do you write the notation if a pawn moves to d1 and gets promoted to a rook?</p>
</li>
<li><p>How do you write the notation if a pawn moves to b8 and gets promoted to a bishop?</p>
</li>
</ul>
<p>And here's the main exercise for this chapter. Show the final board position using these notations:</p>
<ol>
<li><p>e4 e5</p>
</li>
<li><p>Nf3 Nc6</p>
</li>
<li><p>Bb5 Nf6</p>
</li>
<li><p>O-O Nxe4</p>
</li>
<li><p>Re1 Nd6</p>
</li>
<li><p>Nxe5 Be7</p>
</li>
<li><p>Bf1 Nxe5</p>
</li>
<li><p>Rxe5 O-O</p>
</li>
<li><p>d4 Bf6</p>
</li>
<li><p>Re1 Re8</p>
</li>
<li><p>c3 Rxe1</p>
</li>
<li><p>Qxe1 Nf5</p>
</li>
<li><p>Bf4 d6</p>
</li>
<li><p>a4 Qe7</p>
</li>
<li><p>Na3 g5</p>
</li>
<li><p>Bd2 Qxe1</p>
</li>
<li><p>Rxe1 Bd7</p>
</li>
<li><p>a5 Kg7</p>
</li>
</ol>
<h2 id="heading-chapter-2-checkmate-patterns-for-beginners">Chapter 2: <strong>Checkmate Patterns for Beginners</strong></h2>
<p>Before we look at the common checkmate patterns, let's talk a bit about what a check means. We'll do this using the attack pattern or capture method of the pieces.</p>
<h3 id="heading-what-is-a-check-and-checkmate-in-chess"><strong>What is a Check and Checkmate in Chess?</strong></h3>
<p>The main goal of a standard chess match is to capture your opponent's king. You can do this in multiple ways, with different combinations of pieces.</p>
<p>A check is simply an attack on a king. When the king is in check, it must move to a safe square. A safe square for a king in chess is a square where the king is not under attack.</p>
<p>This is an example of a check:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755279141941/1374098d-5c8b-4440-8a4f-724743312b39.png" alt="queen check" class="image--center mx-auto" width="760" height="756" loading="lazy"></p>
<p>The king in the image above is being threatened by the queen. You can deliver checks based on how a piece moves and captures other pieces. A rook can deliver a check like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755279158250/f0c4ce08-5c36-40ab-b4b5-7fe629b36c56.png" alt="rook check" class="image--center mx-auto" width="760" height="755" loading="lazy"></p>
<p>A bishop can deliver a check like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755279174540/cd241bae-1c07-4ba8-a1b9-ecc732102ed3.png" alt="bishop check" class="image--center mx-auto" width="758" height="758" loading="lazy"></p>
<p>Same applies to the knights and pawns – they can check the king based on their movement and mode of attack.</p>
<p>So what is a checkmate? Well, a checkmate happens when the opponent's king has no safe squares to move to after a check. This can be the result of the king being trapped by its own pieces, the king being unable to move to another square because its opponent has control of all the possible squares it can move to, or not having any piece to block the attack on the king.</p>
<p>In this sections that follow, you'll learn about different checkmate patterns using a combination of different pieces.</p>
<h3 id="heading-checkmate-patterns"><strong>Checkmate Patterns</strong></h3>
<h4 id="heading-how-to-play-the-two-rooks-vs-king-checkmate-pattern">How to Play the Two Rooks vs King Checkmate Pattern</h4>
<p>This endgame pattern usually involves two rooks against a king. It is one of the easiest checkmate patterns, but it's also easy to get carried away as a beginner. Here, you'll learn how to effectively mate and end a game with two rooks.</p>
<p>Consider this position:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755279449474/be39b6e4-7816-4717-b17a-534f36c7f9bd.png" alt="two rooks against a king endgame" class="image--center mx-auto" width="760" height="763" loading="lazy"></p>
<p>White can end this game in six moves. Here are some things to keep in mind when you have a position like this:</p>
<ul>
<li><p>Restrict the opposing king's movement by cutting off squares where it can move to.</p>
</li>
<li><p>Keep your rooks away from the opposing king or/and close to each other.</p>
</li>
</ul>
<p>Let's see how each step works using the position above.</p>
<p><strong>Restrict the King:</strong></p>
<p>Looking at the initial position above, you can see that the king is on the f-file. Our goal here is to isolate the king and restrict its movement. One common move for beginners would be to check the king with Rf2+. But this allows the king to move closer to the rook on d4. That is:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755279474239/1a1b715c-09f0-4fc4-9cac-d1f47e48e746.png" alt="two rooks against a king endgame" class="image--center mx-auto" width="757" height="760" loading="lazy"></p>
<p>While this is still a winning position, you'll spend more moves trying to restrict the king's movement. If you're playing a timed game, you may end up with a forced draw, losing a rook, or even losing the game, depending on the pieces on the board. This makes it important to know where and when to move your rooks.</p>
<p>So, back to the starting position:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755279507154/dfc85f37-4d5e-45fe-8246-7adb8e5cae7e.png" alt="two rooks against a king endgame" class="image--center mx-auto" width="760" height="763" loading="lazy"></p>
<ol>
<li>Re2 Kf5.</li>
</ol>
<p>Playing Re2 cuts off the e-file, meaning that the black king can no longer get on any square in that file. Since the king can not pass through or get on the e-file, it means that the a to e files have become inaccessible to it. It is now left with the f to h files.</p>
<p>The king responds with Kf5, an attempt to get closer and capture a rook. At this point, you'd want to stay away from the king.</p>
<p><strong>Move Your Rooks Away From The King:</strong></p>
<p>This is the current position:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755279519416/6635d2ec-2781-48c7-823b-fe8ab3317c5a.png" alt="two rooks against a king endgame" class="image--center mx-auto" width="760" height="757" loading="lazy"></p>
<p>Be careful not to play Re5+, confusing the d-rook for a supporting queen. This forces you into a one rook and king checkmate endgame (you'll learn about that in the next section) after Kxe5. This is a better move:</p>
<ol start="2">
<li>Rd8</li>
</ol>
<p>Which brings us to this position:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755279554729/5938f427-2a49-47f9-9346-9ab589e8b4b3.png" alt="two rooks against a king endgame" class="image--center mx-auto" width="760" height="760" loading="lazy"></p>
<p>This keeps your rooks far away from the black king. Since the king is limited to one square per move, it'll have a long way to go before getting close to either rook. And in the process of getting close, you can continue limiting which files it can get on.</p>
<p>The king has five possible moves here: Kf6, Kf4, Kg6, Kg5, Kg4. The only thing these moves have in common is that they lead to nowhere. But let's assume the black king has some sort of miracle to perform here.</p>
<ol start="2">
<li>... Kg6</li>
</ol>
<p>The king moving to g6 gives you the opportunity to occupy the f-file with:</p>
<ol start="3">
<li>Rf8</li>
</ol>
<p>You have the f-file, but your rook is closer to the king:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755279573831/174f9c17-4caa-48aa-aad6-11eb83f5e13a.png" alt="two rooks against a king endgame" class="image--center mx-auto" width="760" height="757" loading="lazy"></p>
<ol start="3">
<li>... Kg7</li>
</ol>
<p>The king moves closer to challenge the rook. Unfortunately, a checkmate is still inevitable. At the point, you've not only restricted the files that the king can move to, you've also lured it all the way to the seventh rank.</p>
<p><strong>Keep Your Rooks Closer to Each Other:</strong></p>
<ol start="4">
<li>Rf1</li>
</ol>
<p>With one rook on f1 and the other on e2, you're far away from the king and have both rooks closer to each other:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755279765163/b4dd7a02-0356-484d-be33-9ae1b0d1353d.png" alt="two rooks against a king endgame" class="image--center mx-auto" width="757" height="759" loading="lazy"></p>
<p>With this position, it will take the king at least four moves to get to your rooks.</p>
<ol start="4">
<li>... Kg6</li>
</ol>
<p>All that's left is to do now is keep pushing the king away from accessing other files:</p>
<ol start="5">
<li>Rg2+</li>
</ol>
<p>Checking the king with the g-rook forces it to the h-file. Since you have a rook occupying the e-file, the king can’t go there.</p>
<ol start="5">
<li><p>... Kh5</p>
</li>
<li><p>Rh1#</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755279786015/3ba53269-7129-44e0-b3bd-2e994a8e9ef5.png" alt="two rooks against a king checkmate" class="image--center mx-auto" width="878" height="757" loading="lazy"></p>
<p>There are multiple ways to checkmate the king from the starting position, and in some cases, you may already have enough space to bring your rooks together and trap the king, instead of trying to restrict it to a part of the board first.</p>
<p>Consider this position:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755279798809/e3cdade1-b363-4aef-9d45-f34f6b56143f.png" alt="two rooks against a king endgame" class="image--center mx-auto" width="763" height="760" loading="lazy"></p>
<p>The black king is already in a bad position. If you move the b-rook to b6, the game ends after Ra7+ and Rb8#. This did not require you to restrict the king.</p>
<p>Another thing to keep in mind is that your rooks must not always be close to each other to win games like this. Here's another position:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755279808479/26e2581f-b0ee-431a-8d85-ef7d86773e36.png" alt="two rooks against a king endgame" class="image--center mx-auto" width="761" height="758" loading="lazy"></p>
<p>Here, the king is already limited to only the sixth and seventh rank, and you have enough squares/space between it and the rooks to win. There’s no need to avoid the king by moving one rook to a different part of the board.</p>
<p>Playing Rh7+ forces the king into the eighth rank and Ra8# checkmates the king.</p>
<p>You'll also notice that the white king did not participate in these moves. Its contribution was staying out of the way of the rooks. In some cases, the king can be helpful in delivering a checkmate. But if it'll block your rooks from restricting an enemy king, then you should keep your king away.</p>
<p>Here's a replay of the moves, along with the notations:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280037181/5409f0bd-e3ca-4471-b293-8f254bc07116.gif" alt="replay of all moves in two rooks against a king endgame" class="image--center mx-auto" width="758" height="758" loading="lazy"></p>
<ol>
<li><p>Re2 Kf5</p>
</li>
<li><p>Rd8 Kg6</p>
</li>
<li><p>Rf8 Kg7</p>
</li>
<li><p>Rf1 Kg6</p>
</li>
<li><p>Rg2+ Kh5</p>
</li>
<li><p>Rh1#</p>
</li>
</ol>
<p>You can replicate them on your own if you weren't doing so already, and practice with the positions provided below.</p>
<h4 id="heading-practice-position-for-two-rooks-vs-king-endgamecheckmate-pattern">Practice Position for Two Rooks vs King Endgame/Checkmate Pattern</h4>
<p>You can practice what you've learned in the last section using this position:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280062216/a673f5b1-a0a6-4a90-9432-c52ce3ce9109.png" alt="practice position" class="image--center mx-auto" width="759" height="758" loading="lazy"></p>
<p>I’ve set up the position for you here: <a target="_blank" href="https://lichess.org/editor/1r4k1/8/8/5r2/8/3K4/8/8_b_-_-_0_1?color=black">https://lichess.org/editor/1r4k1/8/8/5r2/8/3K4/8/8_b_-_-_0_1?color=black</a>. Click on the link, then click on “CONTINUE FROM HERE” to practice the position against an engine.</p>
<h3 id="heading-how-to-play-the-king-and-rook-vs-king-checkmate-pattern"><strong>How to Play the King and Rook vs King Checkmate Pattern</strong></h3>
<p>Before we talk about how to win this game, you need to learn about the concept of "direct opposition". This happens when two kings are facing each other on the same file or rank, with exactly one square between them. That is:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280143703/c5eae79f-8e20-40a0-847b-d63f3566d361.png" alt="kings in direct opposition " class="image--center mx-auto" width="760" height="762" loading="lazy"></p>
<p>The kings in the image above are facing each other, have one square between them, and are both on the d-file. Both kings are in direct opposition.</p>
<p>Here's another example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280152482/04514349-1f19-4c7b-a2be-e1fce2e74a7b.png" alt="kings in direct opposition " class="image--center mx-auto" width="757" height="758" loading="lazy"></p>
<p>The kings are still facing each other, they also have a square between them, but are now on the seventh rank. There are other forms of opposition like the diagonal and distant opposition, but we won't discuss them here.</p>
<p>So why does direct opposition matter in our current position? Well, let's look at how the king moves.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280163774/098b5c89-946c-4483-9ad6-efb6fe018730.png" alt="all possible king moves" class="image--center mx-auto" width="759" height="759" loading="lazy"></p>
<p>The black king, in the image above, can move to all the squares with a circle on them. Now, here's what happens when the king is in direct opposition with the other king.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280176802/87880756-f232-46db-a978-c415032eec8c.png" alt="all possible king moves when in direct opposition " class="image--center mx-auto" width="758" height="757" loading="lazy"></p>
<p>Now the black king has lost the ability to move to three squares in the direction of the opposition. This also implies that the black king, using the position in the image above, no longer has access to the fourth rank. So playing Ra5+ forces the black king to move to the sixth rank, restricting it, and allowing the white king to come closer and set up another direct opposition.</p>
<p>This is one way of winning with a rook and a king. An alternative method is the box method, which we'll look at later in this section. Both techniques can be used together, but I think it would be helpful to learn them separately at first.</p>
<h4 id="heading-how-to-use-the-direct-opposition-to-win-with-a-rook-and-a-king">How to Use the Direct Opposition to Win with a Rook and a King</h4>
<p>Here's our starting position:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280196692/c088d0a3-fdcb-4156-8cdb-359661d32b3e.png" alt="rook and king vs king" class="image--center mx-auto" width="760" height="760" loading="lazy"></p>
<ol>
<li>Rb5+</li>
</ol>
<p>Forcing the black king to the sixth rank.</p>
<ol>
<li>... Kd6</li>
</ol>
<p>Now the king is trying to get closer and attack your rook, so you have to bring your own king closer for support.</p>
<ol start="2">
<li>Kd4 Ke6</li>
</ol>
<p>Creating direct opposition. If the black king moves backwards (seventh rank), it loses the opportunity to pass through or be on the sixth rank. If it moves to c6, it'll have fewer squares to work with. So the best move for the black king would be Ke6.</p>
<p>Now we'll use something called the box method - a technique used when playing with a rook and a king to shrink the space where the opposing king can move in. This is the current position of the game:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280211256/8cfe2105-00b6-48cb-a630-07dd70708c64.png" alt="rook and king vs king" class="image--center mx-auto" width="763" height="757" loading="lazy"></p>
<ol start="3">
<li>Rd5</li>
</ol>
<p>This move blocks the black king from moving to the d-file and forces it into a box that will gradually shrink if you make the right moves. Here:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280223698/21ddcb13-a973-4e0b-a230-e61d12b5b52c.png" alt="rook and king vs king" class="image--center mx-auto" width="760" height="759" loading="lazy"></p>
<p>The image above shows squares where the black king has been trapped in. The best move for the black king would be Kf6. Playing Ke7 or Kf7 would allow the white king to move closer. Remember, this is a lost position for black, but if you play against an opponent who wants to rely on luck to force a draw through a stalemate or timeout, then knowing exactly where to move your rook becomes very important.</p>
<p>Also, checks with the rook will do you no good in this position. If you check the king with Re5+, then the king will just escape to the d-file. So when you're this close to the opposing king:</p>
<ol start="3">
<li><p>... Kf6</p>
</li>
<li><p>Re5 Kf7</p>
</li>
<li><p>Kd5 Kf6</p>
</li>
</ol>
<p>The black king has gotten closer to your rook again. If you check it, it escapes through the fifth rank. When you have all three pieces in a similar position, move your king instead of a check. This forces the opposing king to remain boxed in.</p>
<ol start="6">
<li><p>Kd6 Kf7</p>
</li>
<li><p>Rf5+ Kg6</p>
</li>
<li><p>Ke6</p>
</li>
</ol>
<p>Providing support to the rook and ensuring that the black king remains in a smaller space. This is what the box looks like now:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280233810/fc4df6f2-0355-42ce-82c4-daba5c30fbfd.png" alt="rook and king vs king" class="image--center mx-auto" width="758" height="757" loading="lazy"></p>
<ol start="8">
<li><p>... Kg7</p>
</li>
<li><p>Rg5+ Kh6</p>
</li>
<li><p>Kf6 Kh7</p>
</li>
<li><p>Rh5+ Kg8</p>
</li>
<li><p>Rh6 Kf8</p>
</li>
<li><p>Rh8#</p>
</li>
</ol>
<p>Here are things to keep in mind when playing with a king and rook against a king:</p>
<ul>
<li><p>Cut off the enemy king with your rook.</p>
</li>
<li><p>Bring your king closer to support the rook and push the enemy king.</p>
</li>
<li><p>Shrink the box using the two points above.</p>
</li>
<li><p>Force the king to the edge of the board (first rank, eight rank, a-file, h-file). Checkmate is only possible when the enemy king is at the edge of the board and in opposition with your king.</p>
</li>
</ul>
<p>This can be overwhelming for beginners, so don't worry if you don't fully understand the process from start to finish. With constant practice, it should become easier and faster for you to recognize the patterns. I'll also provide some puzzles to help you practice.</p>
<p>Here's a replay of all the move and the notations:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280265687/7a572c0c-d47f-422f-ae44-b01e82f42892.gif" alt="rook and king vs king replay" class="image--center mx-auto" width="762" height="758" loading="lazy"></p>
<ol>
<li><p>Rb5+ Kd6</p>
</li>
<li><p>Kd4 Ke6</p>
</li>
<li><p>Rd5 Kf6</p>
</li>
<li><p>Re5 Kf7</p>
</li>
<li><p>Kd5 Kf6</p>
</li>
<li><p>Kd6 Kf7</p>
</li>
<li><p>Rf5+ Kg6</p>
</li>
<li><p>Ke6 Kg7</p>
</li>
<li><p>Rg5+ Kh6</p>
</li>
<li><p>Kf6 Kh7</p>
</li>
<li><p>Rh5+ Kg8</p>
</li>
<li><p>Rh6 Kf8</p>
</li>
<li><p>Rh8#</p>
</li>
</ol>
<h4 id="heading-practice-position-for-one-rook-and-king-vs-king-endgamecheckmate-pattern">Practice Position for One Rook and King vs King Endgame/Checkmate Pattern</h4>
<p>You can practice what you learned in the last section using this position:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280281748/0c091532-cb0d-4e62-8189-f51fbfd01d63.png" alt="rook and king vs king" class="image--center mx-auto" width="759" height="758" loading="lazy"></p>
<p>I’ve set up the position for you here: <a target="_blank" href="https://lichess.org/editor/6k1/8/8/5r2/8/3K4/8/8_b_-_-_0_1?color=black">https://lichess.org/editor/6k1/8/8/5r2/8/3K4/8/8_w_-_-_0_1?color=black</a>. Click on the link, then click on “CONTINUE FROM HERE” to practice the position against an engine.</p>
<h3 id="heading-how-to-play-the-queen-and-king-vs-king-checkmate-pattern">How to Play the Queen and King vs King Checkmate Pattern</h3>
<p>We’ll start with this board:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280311736/272b9d25-3b61-4946-a8ec-5c7fd41e6b03.png" alt="queen and king vs king" class="image--center mx-auto" width="760" height="758" loading="lazy"></p>
<p>There are a couple of ways to play endgame like the one in the image above. A lot of beginners struggle with this checkmate pattern because they often end in a stalemate or just chase the king around the board until they or their opponent runs out of time.</p>
<p>We can approach this endgame using the direct opposition and box method like we did in the previous section, but there's a more efficient and straightforward way of winning this position, with minimal effort from your king.</p>
<p>We'll use the knight move method. This implies that your queen should only move to squares that imitate a knight attack on the enemy king. Using this method, you can push the king towards the edge of the board.</p>
<p>Here are the possible squares where the queen can go to in order to imitate a knight attack:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280323211/2b3f57e3-7383-4557-907c-b2b8daa5f3bb.png" alt="possible knight attack squares for queen" class="image--center mx-auto" width="760" height="761" loading="lazy"></p>
<p>Let's start with the closest square:</p>
<ol>
<li>... Qd6</li>
</ol>
<p>This brings us to this position:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280332247/e23c4fc2-0465-4420-bde5-f7e12164f05a.png" alt="imaginary box for king" class="image--center mx-auto" width="758" height="760" loading="lazy"></p>
<p>We've pushed the king into a box. Now, no matter what the king plays, try to follow up with moves that imitate a knight attack (the L), and do not play a move that allows the king leave the shrinking box.</p>
<ol start="2">
<li>Ke3 Qd5</li>
</ol>
<p>The keeps the king in the box and brings the queen closer.</p>
<ol start="3">
<li><p>Ke2 Qd4</p>
</li>
<li><p>Kf3 Qe5</p>
</li>
</ol>
<p>The box has gotten smaller:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280345278/cc544086-1dd8-46ca-a8c3-7adfa7c820bd.png" alt="imaginary box for king shrinks " class="image--center mx-auto" width="760" height="760" loading="lazy"></p>
<p>No checks. No direct opposition. Just follow the king with the queen, as though you were playing as a knight.</p>
<ol start="5">
<li><p>Kf2 Qe4</p>
</li>
<li><p>Kg3 Qf5</p>
</li>
<li><p>Kg2 Qf4</p>
</li>
<li><p>Kg1 Qf3</p>
</li>
<li><p>Kh2 Qg4</p>
</li>
<li><p>Kh1</p>
</li>
</ol>
<p>You've successfully pushed the king to the edge of the box. At this point, don't be in a hurry to play another knight attack move with the queen – this will lead to a stalemate.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280354553/a96abe4f-cff0-46cd-98d0-844fa33c2a81.png" alt="queen traps kings at the edge of the board" class="image--center mx-auto" width="759" height="758" loading="lazy"></p>
<p>The queen's job is almost done. The white king only has two squares to move to: h2 and h1. It time to push the black king forward to support the queen in a checkmate. Since you've trapped the white king, it will continue to move between the two possible squares until the black king arrives.</p>
<ol start="10">
<li><p>... Ke7</p>
</li>
<li><p>Kh2 Kf6</p>
</li>
<li><p>Kh1 Kf5</p>
</li>
<li><p>Kh2 Kf4</p>
</li>
<li><p>Kh1 Kf3</p>
</li>
<li><p>Kh2 Qg2#</p>
</li>
</ol>
<p>Here's a replay and all the notations:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280366630/10e30dad-f43c-41f1-b3a5-7fca60c3cd4e.gif" alt="replay of queen and king vs king" class="image--center mx-auto" width="758" height="758" loading="lazy"></p>
<ol>
<li><p>... Qd6</p>
</li>
<li><p>Ke3 Qd5</p>
</li>
<li><p>Ke2 Qd4</p>
</li>
<li><p>Kf3 Qe5</p>
</li>
<li><p>Kf2 Qe4</p>
</li>
<li><p>Kg3 Qf5</p>
</li>
<li><p>Kg2 Qf4</p>
</li>
<li><p>Kg1 Qf3</p>
</li>
<li><p>Kh2 Qg4</p>
</li>
<li><p>Kh1 Ke7</p>
</li>
<li><p>Kh2 Kf6</p>
</li>
<li><p>Kh1 Kf5</p>
</li>
<li><p>Kh2 Kf4</p>
</li>
<li><p>Kh1 Kf3</p>
</li>
<li><p>Kh2 Qg2#</p>
</li>
</ol>
<h4 id="heading-practice-position-for-queen-and-king-vs-king-endgamecheckmate-pattern">Practice Position for Queen and King vs King Endgame/Checkmate Pattern</h4>
<p>You can practice what you learned in the last section using this position:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280380835/2a9fee98-d091-4320-9ca5-974ebba98b26.png" alt="practice position for queen and king vs king" class="image--center mx-auto" width="759" height="757" loading="lazy"></p>
<p>I’ve set up the position for you here: <a target="_blank" href="https://lichess.org/editor/6k1/8/8/5q2/8/3K4/8/8_w_-_-_0_1?color=black">https://lichess.org/editor/1r4k1/8/8/5r2/8/3K4/8/8_b_-_-_0_1?color=black</a>. Click on the link, then click on “CONTINUE FROM HERE” to practice the position against an engine.</p>
<p>For this position, choose to play as black before starting the game:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755630967674/d50ef787-4523-48f2-93db-2a8f56825d21.png" alt="choose to play as black" class="image--center mx-auto" width="417" height="698" loading="lazy"></p>
<h3 id="heading-checkmate-patterns-ii">Checkmate Patterns II</h3>
<p>In this section, we’ll deal with checkmate patterns that can happen at any point. That is, you don't have to be in an endgame position/situation to play them.</p>
<p>In some cases, these positions can happen by chance, and in other cases, you have to plan and set them up yourself. The main objective here is to get used to recognizing patterns.</p>
<h4 id="heading-opera-mate">Opera Mate</h4>
<p>An Opera Mate usually involves the following:</p>
<ul>
<li><p>A rook or queen delivering a check along a back rank or file.</p>
</li>
<li><p>A bishop covering a diagonal escape square.</p>
</li>
<li><p>A king trapped by its own pieces or the edge of the board.</p>
</li>
</ul>
<p>Here's an example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280405952/58db2a78-9e42-43e0-a678-2265b4150354.png" alt="opera mate " class="image--center mx-auto" width="761" height="754" loading="lazy"></p>
<p>In the position above, black played Bg7, threatening the white rook. But if we look closely at the position, white can deliver a checkmate by moving to c8.</p>
<p>With Rc8#, the king can't capture the rook because the bishop is protecting it, the king can't move to d7 because the bishop is attacking that square, and unfortunately for the king, its own pawn is blocking it from escaping through e7.</p>
<p>Here's another position:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280417490/2d752769-1af0-41df-898c-1fcad3863374.png" alt="opera mate" class="image--center mx-auto" width="760" height="759" loading="lazy"></p>
<p>Let's assume that it's white's turn to play again. Would it still be checkmate if the rook moves to c8? Yes it would – the position hasn’t really changed: the bishop is still protecting the rook on c8 and attacking the d7 square, and the pawn on e7 is still blocking the king.</p>
<p>Let's look at an example from <a target="_blank" href="https://lichess.org/practice/checkmates/checkmate-patterns-iii/PDkQDt6u/YWsqVJDL">Lichess</a>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280441952/97afbd7b-3618-406e-8dab-d441968bc6bd.png" alt="opera mate " class="image--center mx-auto" width="745" height="740" loading="lazy"></p>
<p>This game still looks alive, as though both players are still setting up their strategies. It's black's turn to play. At this point, we'll assume that black has discovered a possible opera mate.</p>
<p>The black bishop is attacking the d1 and e2 squares, while the queen can check the king on d1, with additional support from the rook on d8. But, a closer look will reveal something: white has a bishop protecting the d1 square.</p>
<p>So, do you go on to attack or play it safe and find a different strategy? Well, let's evaluate the position, keeping the d1 square in mind.</p>
<p>For black:</p>
<ul>
<li><p>Queen can attack d1.</p>
</li>
<li><p>Rook can support and attack d1.</p>
</li>
<li><p>Bishop can support d1.</p>
</li>
</ul>
<p>For white:</p>
<ul>
<li>Bishop is protecting d1.</li>
</ul>
<p>From this evaluation, you can see that black has a very good advantage. In order to win this game, you must sacrifice the queen. Beginners often avoid sacrifices because we learn chess with the idea that the most power pieces must stay on the board. In this case, it’s the only path to victory.</p>
<p>So:</p>
<ol>
<li>... Qd1+</li>
</ol>
<p>That brings us to this position:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280458519/e67e476d-f79e-48e1-9d2f-edfa332d8db1.png" alt="opera mate" class="image--center mx-auto" width="746" height="742" loading="lazy"></p>
<p>Black has two options here:</p>
<ul>
<li><p>Capture the queen with the bishop.</p>
</li>
<li><p>Resign.</p>
</li>
</ul>
<p>Let's go with capturing the queen.</p>
<ol start="2">
<li>Bxd1</li>
</ol>
<p>And now, we have this position:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280468340/3eb6774d-eda7-47d1-9745-4dcc53dd4a30.png" alt="opera mate" class="image--center mx-auto" width="746" height="741" loading="lazy"></p>
<p>Remember the rook on d8?</p>
<ol start="2">
<li>... Rxd1#</li>
</ol>
<p>The rook captures the bishop and wins the game. The king can't capture the rook because it has support from the bishop on g4, it can't escape through e2 because the bishop is attacking that square, and the pawn on f2 is blocking the king.</p>
<p>This shows that you don't have to play until the endgame to win in chess. If you get better at recognizing patterns, you'd be shocked at how many chances you've missed to increase your advantage or win a game.</p>
<h4 id="heading-anastasias-mate">Anastasia's Mate</h4>
<p>Anastasia's Mate is a checkmate pattern that involves a knight and a queen (or rook) working together to trap the opposing king. It involves the following:</p>
<ul>
<li><p>The rook or queen delivering a check.</p>
</li>
<li><p>The knight blocking the king's escape squares (usually two escape squares).</p>
</li>
<li><p>The opposing king trapped by its own pieces.</p>
</li>
</ul>
<p>Consider this position:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280489359/cca92e2e-621f-4f2d-9ead-e491f712f7fc.png" alt="Anastasia's Mate" class="image--center mx-auto" width="759" height="760" loading="lazy"></p>
<p>From the look of things, white has a stronger position. That would be totally true if black didn't have a mate in two opportunity. When black plays Ne2+, it sets up the pattern for Anastasia's mate. That is:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280509251/2d9bf314-9ecc-4265-b884-7249d06a277d.png" alt="Anastasia's Mate" class="image--center mx-auto" width="760" height="759" loading="lazy"></p>
<p>The king has two possible squares to move to: h1 and h2.</p>
<ol>
<li><p>... Ne2+</p>
</li>
<li><p>Kh2</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280547179/a78a17bb-ef6d-42b8-8980-d51a21f93272.png" alt="Anastasia's Mate" class="image--center mx-auto" width="760" height="759" loading="lazy"></p>
<p>Is the king safe?</p>
<ol start="2">
<li>... Rh5#</li>
</ol>
<p>The game ends:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280560054/72452d14-636c-4c4e-815f-cf7f324b87ce.png" alt="Anastasia's Mate" class="image--center mx-auto" width="762" height="756" loading="lazy"></p>
<p>The e2 knight blocks the king from going to either g1 or g3, while the rook attacks the h-file. It's easy to miss this pattern for beginners, because we're often used to endgame patterns that involve more "offensive" attacks.</p>
<p>Here's another example from <a target="_blank" href="https://lichess.org/practice/checkmates/checkmate-patterns-i/fE4k21MW/XOkS95uk">Lichess</a>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280572013/ffa7a5bd-2953-4341-a2a3-e1920a7e9133.png" alt="Anastasia's Mate" class="image--center mx-auto" width="746" height="743" loading="lazy"></p>
<p>It's not very obvious that this position leads to an Anastasia's mate. You have a bishop attacking the queen, and the queen can capture it and gain more material advantage. But what happens when you sacrifice the queen by capturing the h7 square?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280583338/a0b9dac7-2b19-4893-8f44-a66e95c11ca8.png" alt="Anastasia's Mate" class="image--center mx-auto" width="759" height="754" loading="lazy"></p>
<p>You've sacrificed the queen to force the king to the h7 square, setting it up for Rh5#.</p>
<p>So, even though you may recognize what the Anastasia's Mate looks like, you may have to intentionally lose some pieces to achieve it. This is also applicable to other aspects of chess. Sacrificing pieces can be a perfect way to force your opponent into dangerous situations.</p>
<h4 id="heading-back-rank-mate">Back Rank Mate</h4>
<p>This is a checkmate pattern that you'll encounter a lot in your chess journey. It involves trapping the king behind the pieces in front of it. That is:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280622411/5cba4327-6754-4e2c-94ae-760d34f49230.png" alt="Back Rank Mate" class="image--center mx-auto" width="760" height="754" loading="lazy"></p>
<p>From the image above, you can see the white king trapped behind the three pawns in front of it. To avoid this, you should always remember to create space for your king at some point after castling.</p>
<p>Here's an example from <a target="_blank" href="https://lichess.org/analysis/standard/2r1r1k1/5ppp/8/8/Q7/8/5PPP/4R1K1_w_-_-_0_1?color=white">Lichess</a>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280631436/b1a0ddcc-9573-4415-8084-12462ec5abf0.png" alt="Back Rank Mate" class="image--center mx-auto" width="759" height="757" loading="lazy"></p>
<p>It's white's turn to play, and the black king still has three pieces blocking it. The white queen and the rook are both attacking the e8 square, so you can force an exchange that ends the game:</p>
<ol>
<li><p>Rxe8+ Rxe8</p>
</li>
<li><p>Qxe8#</p>
</li>
</ol>
<p>After the trade, you'll have the queen on e8, forcing a checkmate because the king has nowhere to escape to.</p>
<h4 id="heading-smothered-mate">Smothered Mate</h4>
<p>The smothered mate is a checkmate pattern where a knight delivers mate to the enemy king, which is completely trapped/surrounded (or “smothered”) by its own pieces.</p>
<p>Here's what it looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280647104/cb3696e2-aa0f-4114-a8c4-c6d91acded39.png" alt="Smothered Mate" class="image--center mx-auto" width="757" height="759" loading="lazy"></p>
<p>This is a checkmate pattern that shows how powerful the knight can be in endgames. With any other piece, it would be difficult for black to make any significant moves in this situation. But since the knight can jump over pieces when moving and attacking, it can be used in unpredictable ways.</p>
<p>Let's look at an example from <a target="_blank" href="https://lichess.org/practice/checkmates/checkmate-patterns-i/fE4k21MW/UtwzJ9i4">Lichess</a>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280658126/4b53556a-7a96-4e45-9e0b-11ed0c777667.png" alt="Smothered Mate" class="image--center mx-auto" width="759" height="756" loading="lazy"></p>
<p>Again, we'll be sacrificing a piece to gain an advantage. You can sacrifice the white queen with:</p>
<ol>
<li>Qxh7+</li>
</ol>
<p>This forces the black queen to capture:</p>
<ol>
<li>... Qxh7</li>
</ol>
<p>The problem with this is that the king is now trapped:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280674697/43bcbd82-1e7b-4b07-b147-63b213ea23cc.png" alt="Smothered Mate" class="image--center mx-auto" width="759" height="755" loading="lazy"></p>
<p>As beginners, it's often easier to capture a powerful piece than to analyze a position for potential mate patterns. It wouldn’t be surprising to see someone new to chess capturing the queen with the knight.</p>
<p>The best move here is Nf7#:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755280684007/f57d19c2-9072-4cde-b732-93dc5c73a861.png" alt="Smothered Mate" class="image--center mx-auto" width="759" height="758" loading="lazy"></p>
<p>The king is under check and can't escape because it has been trapped by its own pieces.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The handbook showed you some of the common checkmate patterns in chess. But these are not all of them, and not all checkmate patterns are easy to understand for beginners.</p>
<p>While you improve other aspects of your game like strategy and tactics, opening principles, solving puzzles, and so on, it becomes easier to understand certain concepts that seemed too hard at first.</p>
<p>One way to test your understanding of different concepts is by playing against other people or engines. A good place to do this is on Lichess. It’s free, has an active community, and a ton of tools to help you improve your game.</p>
<p>I hope this handbook has been helpful for you on your chess journey. You can <a target="_blank" href="https://lichess.org/@/IHECHIKARA">find me on Lichess @ IHECHIKARA</a>.</p>
<p>You can <a target="_blank" href="https://youtu.be/1AX7_aPWmbE?si=67txQ7Bc-IHZ0lhW">watch the video version of this handbook here</a>.</p>
<h2 id="heading-exercise-answers">Exercise Answers</h2>
<p>Here are the answers to the exercises and practice positions:</p>
<h4 id="heading-files-a-to-f-exercise">Files a to f Exercise</h4>
<p>The arrow is on the c-file. You can tell by looking at the alphabet that corresponds with the vertical line.</p>
<h4 id="heading-ranks-1-to-8-exercise">Ranks 1 to 8 Exercise</h4>
<p>The arrow is on the eight rank. You can tell by looking at the number that corresponds with the horizontal line.</p>
<h4 id="heading-how-to-denote-piece-promotion-in-algebraic-chess-notation">How to Denote Piece Promotion in Algebraic Chess Notation</h4>
<ul>
<li><p>The notation for a pawn that moves to d1 and gets promoted to a rook is d1=R.</p>
</li>
<li><p>The notation for a pawn that moves to b8 and gets promoted to a bishop d1=B.</p>
</li>
<li><p>Here is the final board position for the given notations:</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755632639415/467a3c95-c234-4022-81fd-13867fa6b5ca.png" alt="final position for the given notations in the algebraic chess notations section" class="image--center mx-auto" width="790" height="793" loading="lazy"></p>
<h2 id="heading-practice-position-urls">Practice Position URLs</h2>
<p>Here are the links for all the practice positions:</p>
<ul>
<li><p>Two Rooks vs King Endgame/Checkmate Pattern URL: <a target="_blank" href="https://lichess.org/editor/1r4k1/8/8/5r2/8/3K4/8/8_b_-_-_0_1?color=black">https://lichess.org/editor/1r4k1/8/8/5r2/8/3K4/8/8_b_-_-_0_1?color=black</a>.</p>
</li>
<li><p>One Rook and King vs King Endgame/Checkmate Pattern URL: <a target="_blank" href="https://lichess.org/editor/6k1/8/8/5r2/8/3K4/8/8_b_-_-_0_1?color=black">https://lichess.org/editor/6k1/8/8/5r2/8/3K4/8/8_w_-_-_0_1?color=black</a>.</p>
</li>
<li><p>Queen and King vs King Endgame/Checkmate Pattern URL: <a target="_blank" href="https://lichess.org/editor/6k1/8/8/5q2/8/3K4/8/8_w_-_-_0_1?color=black">https://lichess.org/editor/1r4k1/8/8/5r2/8/3K4/8/8_b_-_-_0_1?color=black</a> (choose to play as black).</p>
</li>
<li><p>Practice many checkmate patterns here: <a target="_blank" href="https://lichess.org/practice/checkmates/checkmate-patterns-i/fE4k21MW/9rd7XwOw">https://lichess.org/practice/checkmates/checkmate-patterns-i/fE4k21MW/9rd7XwOw</a>. Click on “Practice list” to access more patterns.</p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use MongoDB with Go ]]>
                </title>
                <description>
                    <![CDATA[ Working with databases is a fundamental part of backend development, particularly when you’re building applications that require persisting, querying, and updating data. In Go, the official MongoDB driver provides a robust way to connect to and inter... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-mongodb-with-go/</link>
                <guid isPermaLink="false">688b03e7d853646bf4658392</guid>
                
                    <category>
                        <![CDATA[ Go Language ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MongoDB ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Oluwadamilola Oshungboye ]]>
                </dc:creator>
                <pubDate>Thu, 31 Jul 2025 05:49:27 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1753940956019/6551a007-b463-486f-8746-15c13a7a99a0.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Working with databases is a fundamental part of backend development, particularly when you’re building applications that require persisting, querying, and updating data.</p>
<p>In Go, the <a target="_blank" href="https://github.com/mongodb/mongo-go-driver">official MongoDB driver</a> provides a robust way to connect to and interact with MongoDB, a flexible NoSQL database that stores data in JSON-like documents.</p>
<p>In this tutorial, you won't just learn how to connect Go to MongoDB. You'll take it a step further by building a simple blog application. Along the way, you'll learn how to perform essential CRUD (Create, Read, Update, Delete) operations and display your results using the Gin web framework.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-create-a-new-go-project">Create a New Go Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-basic-mongodb-operations">Basic MongoDB Operations</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-insert-data-into-the-collection">Insert data into the collection</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-find-documents-in-mongodb">Find documents in MongoDB</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-update-documents-in-mongodb">Update documents in MongoDB</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-delete-documents-in-mongodb">Delete documents in MongoDB</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-a-blog-app-with-go-mongodb-driver-and-gin">How to Build a Blog App with go-mongodb-driver and Gin</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-initialize-the-gin-application">Initialize the Gin application</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-create-the-html-templates">Create the HTML templates</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-create-the-handlers">Create the handlers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-run-the-application">Run the application</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-thats-how-to-use-mongodb-with-go">That's How to Use MongoDB with Go</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before you proceed, ensure that you have the following:</p>
<ul>
<li><p>Basic knowledge of <a target="_blank" href="https://go.dev/">Go</a> and its concepts</p>
</li>
<li><p>Go (version 1.24 or higher) installed</p>
</li>
<li><p><a target="_blank" href="https://www.mongodb.com/docs/manual/installation/">MongoDB</a> Installed (running locally on port 27017)</p>
</li>
<li><p>Basic knowledge of NoSQL</p>
</li>
</ul>
<h2 id="heading-create-a-new-go-project">Create a New Go Project</h2>
<p>First, create a new Go project, change into the new project directory, and initialize a new Go module by running the following commands:</p>
<pre><code class="lang-bash">mkdir go-mongodb-integration
<span class="hljs-built_in">cd</span> go-mongodb-integrationgo 
mod init go-mongodb
</code></pre>
<p>Next, install the MongoDB Go driver by running the following command:</p>
<pre><code class="lang-bash">go get go.mongodb.org/mongo-driver/mongo
go get go.mongodb.org/mongo-driver/bson
</code></pre>
<p>The standard Go library includes the <code>database/sql</code> package for working with SQL databases, but it doesn't support MongoDB out of the box. To work with MongoDB in Go, you’ll use the official MongoDB driver, which provides everything you need to connect to and interact with a MongoDB database.</p>
<p>With the basic setup complete, let’s now examine basic operations in MongoDB.</p>
<h2 id="heading-basic-mongodb-operations">Basic MongoDB Operations</h2>
<p>In MongoDB, databases and <a target="_blank" href="https://www.mongodb.com/docs/manual/reference/glossary/#std-term-collection">collections</a> are created automatically upon the first data insertion, adopting a "lazy creation" approach. Specifically, a database is created when you insert your first document, and a collection is likewise created when data is first inserted into it.</p>
<p>It's important to note that functions like <code>client.Database()</code> and <code>db.Collection()</code> only generate references to these structures – they don’t create the actual database or collection until data is inserted.</p>
<h3 id="heading-insert-data-into-the-collection">Insert data into the collection</h3>
<p>Let’s walk through how to insert a document into a collection in MongoDB.</p>
<p>First, open your project in a code editor, create a <code>main.go</code> file, and add the following code:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"context"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"time"</span>

    <span class="hljs-string">"go.mongodb.org/mongo-driver/bson/primitive"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/mongo"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/mongo/options"</span>
)

<span class="hljs-keyword">type</span> User <span class="hljs-keyword">struct</span> {
    ID   primitive.ObjectID <span class="hljs-string">`bson:"_id,omitempty"`</span>
    Name <span class="hljs-keyword">string</span>             <span class="hljs-string">`bson:"name"`</span>
    Age  <span class="hljs-keyword">int</span>                <span class="hljs-string">`bson:"age"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    clientOptions := options.Client().ApplyURI(<span class="hljs-string">"mongodb://localhost:27017"</span>)

    ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">10</span>*time.Second)
    <span class="hljs-keyword">defer</span> cancel()

    client, err := mongo.Connect(ctx, clientOptions)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    <span class="hljs-keyword">defer</span> client.Disconnect(ctx)

    err = client.Ping(ctx, <span class="hljs-literal">nil</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }

    db := client.Database(<span class="hljs-string">"test_db"</span>)
    usersCollection := db.Collection(<span class="hljs-string">"users"</span>)

    newUser := User{
        Name: <span class="hljs-string">"John Doe"</span>,
        Age:  <span class="hljs-number">30</span>,
    }

    result, err := usersCollection.InsertOne(ctx, newUser)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }

    log.Printf(<span class="hljs-string">"Inserted user with ID: %v\n"</span>, result.InsertedID)
}
</code></pre>
<p>In the code above, you define a <code>User</code> <a target="_blank" href="https://www.w3schools.com/go/go_struct.php">struct</a> that represents your document structure, then insert a new user document into the collection using the <code>InsertOne</code> method. When you run this insert operation, MongoDB automatically creates both the <code>test_db</code> database and the <code>users</code> collection if they don’t already exist.</p>
<p>Execute the code by running:</p>
<pre><code class="lang-bash">go run main.go
</code></pre>
<p>You should see the response below, indicating that a user was inserted successfully.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753561045916/79c1f4f1-ebe1-41e8-b6dd-01a5e2c9d247.png" alt="A command line interface showing the command `go run main.go` with an output that says, &quot;Inserted user with ID: ObjectID('6862f3112341b0492801633b')&quot; on June 30, 2025." class="image--center mx-auto" width="1068" height="130" loading="lazy"></p>
<h3 id="heading-find-documents-in-mongodb">Find documents in MongoDB</h3>
<p>Now that you've inserted some data, it's time to query the database and retrieve documents.</p>
<p>Update your <code>main.go</code> file with the following code:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"context"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"time"</span>

    <span class="hljs-string">"go.mongodb.org/mongo-driver/bson"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/bson/primitive"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/mongo"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/mongo/options"</span>
)

<span class="hljs-keyword">type</span> User <span class="hljs-keyword">struct</span> {
    ID   primitive.ObjectID <span class="hljs-string">`bson:"_id,omitempty"`</span>
    Name <span class="hljs-keyword">string</span>             <span class="hljs-string">`bson:"name"`</span>
    Age  <span class="hljs-keyword">int</span>                <span class="hljs-string">`bson:"age"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    clientOptions := options.Client().ApplyURI(<span class="hljs-string">"mongodb://localhost:27017"</span>)

    ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">10</span>*time.Second)
    <span class="hljs-keyword">defer</span> cancel()

    client, err := mongo.Connect(ctx, clientOptions)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    <span class="hljs-keyword">defer</span> client.Disconnect(ctx)

    db := client.Database(<span class="hljs-string">"test_db"</span>)
    usersCollection := db.Collection(<span class="hljs-string">"users"</span>)

    cursor, err := usersCollection.Find(ctx, bson.M{})
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    <span class="hljs-keyword">defer</span> cursor.Close(ctx)

    <span class="hljs-keyword">var</span> users []User
    <span class="hljs-keyword">if</span> err = cursor.All(ctx, &amp;users); err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }

    <span class="hljs-keyword">for</span> _, user := <span class="hljs-keyword">range</span> users {
        fmt.Printf(<span class="hljs-string">"User: %s, Age: %d, ID: %s\n"</span>, user.Name, user.Age, user.ID.Hex())
    }
}
</code></pre>
<p>In the code above, you use the <code>Find</code> method with an empty filter (`bson.M{}`) to retrieve all documents from the <code>users</code> collection. Then, you use <code>cursor.All</code> to decode all the results into a slice of <code>User</code> structs.</p>
<p>Each document is printed to the terminal, showing the name, age, and ID of every user in the collection.</p>
<p>To run the code, use:</p>
<pre><code class="lang-bash">go run main.go
</code></pre>
<p>You should see the response below in your terminal.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753561158932/adc32e01-742e-4c5c-acf8-a83e1197a27c.png" alt="Screenshot of a terminal showing a Go command execution and output with user information including name, age, and ID." width="512" height="83" loading="lazy"></p>
<h3 id="heading-update-documents-in-mongodb">Update documents in MongoDB</h3>
<p>To update a document in your collection, modify your <code>main.go</code> file as shown below:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"context"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"time"</span>

    <span class="hljs-string">"go.mongodb.org/mongo-driver/bson"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/bson/primitive"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/mongo"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/mongo/options"</span>
)

<span class="hljs-keyword">type</span> User <span class="hljs-keyword">struct</span> {
    ID   primitive.ObjectID <span class="hljs-string">`bson:"_id,omitempty"`</span>
    Name <span class="hljs-keyword">string</span>             <span class="hljs-string">`bson:"name"`</span>
    Age  <span class="hljs-keyword">int</span>                <span class="hljs-string">`bson:"age"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    clientOptions := options.Client().ApplyURI(<span class="hljs-string">"mongodb://localhost:27017"</span>)
    ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">10</span>*time.Second)
    <span class="hljs-keyword">defer</span> cancel()

    client, err := mongo.Connect(ctx, clientOptions)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    <span class="hljs-keyword">defer</span> client.Disconnect(ctx)

    db := client.Database(<span class="hljs-string">"test_db"</span>)
    usersCollection := db.Collection(<span class="hljs-string">"users"</span>)

    <span class="hljs-keyword">var</span> userToUpdate User
    err = usersCollection.FindOne(ctx, bson.M{<span class="hljs-string">"name"</span>: <span class="hljs-string">"John Doe"</span>}).Decode(&amp;userToUpdate)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Println(<span class="hljs-string">"No user found to update"</span>)
    } <span class="hljs-keyword">else</span> {
        update := bson.M{
            <span class="hljs-string">"$set"</span>: bson.M{
                <span class="hljs-string">"name"</span>: <span class="hljs-string">"Jane Doe"</span>,
                <span class="hljs-string">"age"</span>:  <span class="hljs-number">25</span>,
            },
        }
        result, err := usersCollection.UpdateOne(
            ctx,
            bson.M{<span class="hljs-string">"_id"</span>: userToUpdate.ID},
            update,
        )
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            log.Fatal(err)
        }
        log.Printf(<span class="hljs-string">"Updated %v document(s)\n"</span>, result.ModifiedCount)
    }
}
</code></pre>
<p>In the code above, you first search for a user named "John Doe" using the <code>FindOne</code> method. If a match is found, you use the <code>UpdateOne</code> method to update their name and age. The <code>$set</code> operator ensures that only the specified fields are updated, leaving the rest of the document unchanged.</p>
<p>Execute the code by running:</p>
<pre><code class="lang-bash">go run main.go
</code></pre>
<p>You should see output in your terminal indicating how many documents were updated.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753561280040/73f7a859-5d7a-440a-a58b-b6105b8837ab.png" alt="Command line interface showing &quot;go run main.go&quot; and the output &quot;2025/06/30 21:34:36 Updated 1 document(s)&quot;." width="512" height="89" loading="lazy"></p>
<h3 id="heading-delete-documents-in-mongodb">Delete documents in MongoDB</h3>
<p>To remove documents from a collection, you can use the <code>DeleteOne</code> method. Update your <code>main.go</code> file with the following code:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"context"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"time"</span>

    <span class="hljs-string">"go.mongodb.org/mongo-driver/bson"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/bson/primitive"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/mongo"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/mongo/options"</span>
)

<span class="hljs-keyword">type</span> User <span class="hljs-keyword">struct</span> {
    ID   primitive.ObjectID <span class="hljs-string">`bson:"_id,omitempty"`</span>
    Name <span class="hljs-keyword">string</span>             <span class="hljs-string">`bson:"name"`</span>
    Age  <span class="hljs-keyword">int</span>                <span class="hljs-string">`bson:"age"`</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    clientOptions := options.Client().ApplyURI(<span class="hljs-string">"mongodb://localhost:27017"</span>)
    ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">10</span>*time.Second)
    <span class="hljs-keyword">defer</span> cancel()

    client, err := mongo.Connect(ctx, clientOptions)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    <span class="hljs-keyword">defer</span> client.Disconnect(ctx)

    db := client.Database(<span class="hljs-string">"test_db"</span>)
    usersCollection := db.Collection(<span class="hljs-string">"users"</span>)

    result, err := usersCollection.DeleteOne(ctx, bson.M{<span class="hljs-string">"name"</span>: <span class="hljs-string">"Jane Doe"</span>})
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    log.Printf(<span class="hljs-string">"Deleted %v document(s)\n"</span>, result.DeletedCount)
}
</code></pre>
<p>In the code above, you use the <code>DeleteOne</code> method to remove the first document that matches the filter <code>{ "name": "Jane Doe" }</code>.</p>
<p>You should see the result below in your terminal.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753561355070/ab8797ef-8a15-47e0-8502-9b120fbbd2b8.png" alt="A command line terminal showing the execution of &quot;go run main.go&quot; and outputting &quot;2025/06/30 21:36:05 Deleted 1 document(s)&quot;." width="512" height="84" loading="lazy"></p>
<h2 id="heading-how-to-build-a-blog-app-with-go-mongodb-driver-and-gin">How to Build a Blog App with go-mongodb-driver and Gin</h2>
<p>Now that you understand how to perform basic CRUD operations with MongoDB in Go, you're ready to build a more complete application.</p>
<p>Start by creating a new directory for your project and initializing it as a Go module. Navigate to your chosen directory and run:</p>
<pre><code class="lang-bash">mkdir go-blog
<span class="hljs-built_in">cd</span> go-blog
go mod init blog
</code></pre>
<p>Next, install the required dependencies:</p>
<pre><code class="lang-bash">go get github.com/gin-gonic/gin
go get go.mongodb.org/mongo-driver/mongo
go get go.mongodb.org/mongo-driver/bson
</code></pre>
<p>Your project will have the following structure:</p>
<pre><code class="lang-bash">go-blog/  
├── main.go  
├── handlers/  
│   └── main.go  
└── templates/  
    ├── index.html  
    ├── post.html  
    ├── create.html  
    └── edit.html
</code></pre>
<h3 id="heading-initialize-the-gin-application">Initialize the Gin application</h3>
<p>To initialize a new Gin application, create a new <code>main.go</code> file and add the below code snippet to it:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"context"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"time"</span>

    <span class="hljs-string">"blog/handlers"</span>

    <span class="hljs-string">"github.com/gin-gonic/gin"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/mongo"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/mongo/options"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">10</span>*time.Second)
    <span class="hljs-keyword">defer</span> cancel()

    clientOptions := options.Client().ApplyURI(<span class="hljs-string">"mongodb://localhost:27017"</span>)
    client, err := mongo.Connect(ctx, clientOptions)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    <span class="hljs-keyword">defer</span> client.Disconnect(ctx)

    err = client.Ping(ctx, <span class="hljs-literal">nil</span>)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        log.Fatal(err)
    }
    log.Println(<span class="hljs-string">"Connected to MongoDB!"</span>)

    db := client.Database(<span class="hljs-string">"blog_db"</span>)
    h := handlers.NewHandler(db)

    router := gin.Default()
    router.LoadHTMLGlob(<span class="hljs-string">"templates/*"</span>)

    router.GET(<span class="hljs-string">"/"</span>, h.HomePage)
    router.GET(<span class="hljs-string">"/post/:id"</span>, h.ViewPost)
    router.GET(<span class="hljs-string">"/create"</span>, h.CreatePost)
    router.GET(<span class="hljs-string">"/edit/:id"</span>, h.EditPost)
    router.POST(<span class="hljs-string">"/save"</span>, h.SavePost)
    router.GET(<span class="hljs-string">"/delete/:id"</span>, h.DeletePost)

    log.Println(<span class="hljs-string">"Server starting on :8080..."</span>)
    router.Run(<span class="hljs-string">":8080"</span>)
}
</code></pre>
<p>The code above sets up the MongoDB connection, initializes the Gin router, and registers your routes.</p>
<h3 id="heading-create-the-html-templates">Create the HTML templates</h3>
<p>Now, create the HTML templates for displaying the blog UI.</p>
<p>First, create a <code>templates</code> directory and add the following files:</p>
<h4 id="heading-indexhtml">index.html:</h4>
<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>Go Blog with MongoDB<span class="hljs-tag">&lt;/<span class="hljs-name">title</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.tailwindcss.com"</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> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-100 min-h-screen"</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 mx-auto px-4 py-8"</span>&gt;</span>  
        <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-8"</span>&gt;</span>  
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-3xl font-bold text-center text-blue-600"</span>&gt;</span>Go Blog with MongoDB<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">"flex justify-center mt-4"</span>&gt;</span>  
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/create"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded"</span>&gt;</span>Create New Post<span class="hljs-tag">&lt;/<span class="hljs-name">a</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">header</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"</span>&gt;</span>  
            {{range .}}  
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300"</span>&gt;</span>  
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-6"</span>&gt;</span>  
                    <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xl font-semibold mb-2 text-gray-800"</span>&gt;</span>{{.Title}}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</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-gray-600 mb-4 line-clamp-3"</span>&gt;</span>  
                        {{if gt (len .Content) 150}}  
                            {{slice .Content 0 150}}...  
                        {{else}}  
                            {{.Content}}  
                        {{end}}  
                    <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">class</span>=<span class="hljs-string">"flex justify-between items-center text-sm text-gray-500"</span>&gt;</span>  
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{{.CreatedAt.Format "Jan 02, 2006"}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>  
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/post/{{.ID.Hex}}"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-blue-500 hover:text-blue-700"</span>&gt;</span>Read More<span class="hljs-tag">&lt;/<span class="hljs-name">a</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">class</span>=<span class="hljs-string">"flex border-t border-gray-200"</span>&gt;</span>  
                    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/edit/{{.ID.Hex}}"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-1/2 py-2 text-center text-sm text-gray-600 hover:bg-gray-100 border-r border-gray-200"</span>&gt;</span>Edit<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>  
                    <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/delete/{{.ID.Hex}}"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-1/2 py-2 text-center text-sm text-red-600 hover:bg-gray-100"</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"return confirm('Are you sure you want to delete this post?')"</span>&gt;</span>Delete<span class="hljs-tag">&lt;/<span class="hljs-name">a</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>  
            {{else}}  
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"col-span-3 text-center py-12"</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-gray-600 text-lg"</span>&gt;</span>No posts yet. <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/create"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-blue-500 hover:underline"</span>&gt;</span>Create one<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">div</span>&gt;</span>  
            {{end}}  
        <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">body</span>&gt;</span>  
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This template lists all blog posts and includes buttons to create, edit, or delete posts.</p>
<h4 id="heading-posthtml">post.html:</h4>
<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>{{.Title}} | Go Blog with MongoDB<span class="hljs-tag">&lt;/<span class="hljs-name">title</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.tailwindcss.com"</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> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-100 min-h-screen"</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 mx-auto px-4 py-8"</span>&gt;</span>  
        <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-8"</span>&gt;</span>  
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-3xl font-bold text-center text-blue-600"</span>&gt;</span>Go Blog with MongoDB<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">"flex justify-center mt-4"</span>&gt;</span>  
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded"</span>&gt;</span>Back to Home<span class="hljs-tag">&lt;/<span class="hljs-name">a</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">header</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-3xl mx-auto bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>  
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-6"</span>&gt;</span>  
                <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl font-bold mb-4 text-gray-800"</span>&gt;</span>{{.Title}}<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">class</span>=<span class="hljs-string">"flex items-center text-sm text-gray-500 mb-6"</span>&gt;</span>  
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>Posted: {{.CreatedAt.Format "Jan 02, 2006"}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>  
                    {{if ne .CreatedAt .UpdatedAt}}  
                    <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mx-2"</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">span</span>&gt;</span>Updated: {{.UpdatedAt.Format "Jan 02, 2006"}}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>  
                    {{end}}  
                <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">"prose max-w-none"</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-gray-700 whitespace-pre-line"</span>&gt;</span>{{.Content}}<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">class</span>=<span class="hljs-string">"flex border-t border-gray-200"</span>&gt;</span>  
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/edit/{{.ID.Hex}}"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-1/2 py-3 text-center text-blue-600 hover:bg-gray-100 border-r border-gray-200"</span>&gt;</span>Edit Post<span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>  
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/delete/{{.ID.Hex}}"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"w-1/2 py-3 text-center text-red-600 hover:bg-gray-100"</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"return confirm('Are you sure you want to delete this post?')"</span>&gt;</span>Delete Post<span class="hljs-tag">&lt;/<span class="hljs-name">a</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">body</span>&gt;</span>  
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This template displays a single post.</p>
<h4 id="heading-createhtml">create.html:</h4>
<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>Create New Post | Go Blog with MongoDB<span class="hljs-tag">&lt;/<span class="hljs-name">title</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.tailwindcss.com"</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> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-100 min-h-screen"</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 mx-auto px-4 py-8"</span>&gt;</span>  
        <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-8"</span>&gt;</span>  
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-3xl font-bold text-center text-blue-600"</span>&gt;</span>Go Blog with MongoDB<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">"flex justify-center mt-4"</span>&gt;</span>  
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded"</span>&gt;</span>Back to Home<span class="hljs-tag">&lt;/<span class="hljs-name">a</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">header</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-2xl mx-auto bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>  
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-6"</span>&gt;</span>  
                <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl font-bold mb-6 text-gray-800"</span>&gt;</span>Create New Post<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>  

                <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"/save"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"POST"</span>&gt;</span>  
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-4"</span>&gt;</span>  
                        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"title"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-gray-700 font-medium mb-2"</span>&gt;</span>Title<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">id</span>=<span class="hljs-string">"title"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"title"</span> <span class="hljs-attr">required</span>  
                            <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"</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">"mb-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">"content"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-gray-700 font-medium mb-2"</span>&gt;</span>Content<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>  
                        <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"content"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"content"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">required</span>  
                            <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">"flex justify-end"</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">"bg-blue-500 hover:bg-blue-600 text-white px-6 py-2 rounded-md"</span>&gt;</span>  
                            Save Post  
                        <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">form</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">body</span>&gt;</span>  
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This template allows you to create a new post.</p>
<h4 id="heading-edithtml">edit.html:</h4>
<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>Edit Post | Go Blog with MongoDB<span class="hljs-tag">&lt;/<span class="hljs-name">title</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.tailwindcss.com"</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> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-gray-100 min-h-screen"</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 mx-auto px-4 py-8"</span>&gt;</span>  
        <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-8"</span>&gt;</span>  
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-3xl font-bold text-center text-blue-600"</span>&gt;</span>Go Blog with MongoDB<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">"flex justify-center mt-4"</span>&gt;</span>  
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded"</span>&gt;</span>Back to Home<span class="hljs-tag">&lt;/<span class="hljs-name">a</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">header</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"max-w-2xl mx-auto bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>  
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-6"</span>&gt;</span>  
                <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-2xl font-bold mb-6 text-gray-800"</span>&gt;</span>Edit Post<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>  

                <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"/save"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"POST"</span>&gt;</span>  
                    <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"hidden"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"id"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"{{.ID.Hex}}"</span>&gt;</span>  

                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"mb-4"</span>&gt;</span>  
                        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">for</span>=<span class="hljs-string">"title"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-gray-700 font-medium mb-2"</span>&gt;</span>Title<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">id</span>=<span class="hljs-string">"title"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"title"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"{{.Title}}"</span> <span class="hljs-attr">required</span>  
                            <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"</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">"mb-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">"content"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"block text-gray-700 font-medium mb-2"</span>&gt;</span>Content<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>  
                        <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"content"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"content"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"10"</span> <span class="hljs-attr">required</span>  
                            <span class="hljs-attr">class</span>=<span class="hljs-string">"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"</span>&gt;</span>{{.Content}}<span class="hljs-tag">&lt;/<span class="hljs-name">textarea</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">"flex justify-between"</span>&gt;</span>  
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/post/{{.ID.Hex}}"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-gray-600 hover:text-gray-800"</span>&gt;</span>Cancel<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">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-blue-500 hover:bg-blue-600 text-white px-6 py-2 rounded-md"</span>&gt;</span>  
                            Update Post  
                        <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">form</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">body</span>&gt;</span>  
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>This template is used to edit a post.</p>
<h3 id="heading-create-the-handlers">Create the handlers</h3>
<p>Next, set up the handlers to connect with MongoDB and render the templates. Create a new folder called <code>handlers</code> in your project's root directory, then add a <code>main.go</code> file inside it and insert the following code snippet:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> handlers

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"context"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"time"</span>

    <span class="hljs-string">"github.com/gin-gonic/gin"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/bson"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/bson/primitive"</span>
    <span class="hljs-string">"go.mongodb.org/mongo-driver/mongo"</span>
)

<span class="hljs-keyword">type</span> Post <span class="hljs-keyword">struct</span> {
    ID        primitive.ObjectID <span class="hljs-string">`bson:"_id,omitempty" json:"id"`</span>
    Title     <span class="hljs-keyword">string</span>             <span class="hljs-string">`bson:"title" json:"title"`</span>
    Content   <span class="hljs-keyword">string</span>             <span class="hljs-string">`bson:"content" json:"content"`</span>
    CreatedAt time.Time          <span class="hljs-string">`bson:"created_at" json:"created_at"`</span>
    UpdatedAt time.Time          <span class="hljs-string">`bson:"updated_at" json:"updated_at"`</span>
}

<span class="hljs-keyword">type</span> Handler <span class="hljs-keyword">struct</span> {
    db         *mongo.Database
    collection *mongo.Collection
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewHandler</span><span class="hljs-params">(db *mongo.Database)</span> *<span class="hljs-title">Handler</span></span> {
    <span class="hljs-keyword">return</span> &amp;Handler{
        db:         db,
        collection: db.Collection(<span class="hljs-string">"posts"</span>),
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(h *Handler)</span> <span class="hljs-title">HomePage</span><span class="hljs-params">(c *gin.Context)</span></span> {
    ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">5</span>*time.Second)
    <span class="hljs-keyword">defer</span> cancel()

    cursor, err := h.collection.Find(ctx, bson.M{})
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        c.JSON(http.StatusInternalServerError, gin.H{<span class="hljs-string">"error"</span>: err.Error()})
        <span class="hljs-keyword">return</span>
    }
    <span class="hljs-keyword">defer</span> cursor.Close(ctx)

    <span class="hljs-keyword">var</span> posts []Post
    <span class="hljs-keyword">if</span> err = cursor.All(ctx, &amp;posts); err != <span class="hljs-literal">nil</span> {
        c.JSON(http.StatusInternalServerError, gin.H{<span class="hljs-string">"error"</span>: err.Error()})
        <span class="hljs-keyword">return</span>
    }

    c.HTML(http.StatusOK, <span class="hljs-string">"index.html"</span>, posts)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(h *Handler)</span> <span class="hljs-title">ViewPost</span><span class="hljs-params">(c *gin.Context)</span></span> {
    id := c.Param(<span class="hljs-string">"id"</span>)
    objID, err := primitive.ObjectIDFromHex(id)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        c.JSON(http.StatusBadRequest, gin.H{<span class="hljs-string">"error"</span>: <span class="hljs-string">"Invalid post ID"</span>})
        <span class="hljs-keyword">return</span>
    }

    ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">5</span>*time.Second)
    <span class="hljs-keyword">defer</span> cancel()

    <span class="hljs-keyword">var</span> post Post
    err = h.collection.FindOne(ctx, bson.M{<span class="hljs-string">"_id"</span>: objID}).Decode(&amp;post)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        c.JSON(http.StatusNotFound, gin.H{<span class="hljs-string">"error"</span>: <span class="hljs-string">"Post not found"</span>})
        <span class="hljs-keyword">return</span>
    }

    c.HTML(http.StatusOK, <span class="hljs-string">"post.html"</span>, post)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(h *Handler)</span> <span class="hljs-title">CreatePost</span><span class="hljs-params">(c *gin.Context)</span></span> {
    c.HTML(http.StatusOK, <span class="hljs-string">"create.html"</span>, <span class="hljs-literal">nil</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(h *Handler)</span> <span class="hljs-title">EditPost</span><span class="hljs-params">(c *gin.Context)</span></span> {
    id := c.Param(<span class="hljs-string">"id"</span>)
    objID, err := primitive.ObjectIDFromHex(id)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        c.JSON(http.StatusBadRequest, gin.H{<span class="hljs-string">"error"</span>: <span class="hljs-string">"Invalid post ID"</span>})
        <span class="hljs-keyword">return</span>
    }

    ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">5</span>*time.Second)
    <span class="hljs-keyword">defer</span> cancel()

    <span class="hljs-keyword">var</span> post Post
    err = h.collection.FindOne(ctx, bson.M{<span class="hljs-string">"_id"</span>: objID}).Decode(&amp;post)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        c.JSON(http.StatusNotFound, gin.H{<span class="hljs-string">"error"</span>: <span class="hljs-string">"Post not found"</span>})
        <span class="hljs-keyword">return</span>
    }

    c.HTML(http.StatusOK, <span class="hljs-string">"edit.html"</span>, post)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(h *Handler)</span> <span class="hljs-title">SavePost</span><span class="hljs-params">(c *gin.Context)</span></span> {
    ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">5</span>*time.Second)
    <span class="hljs-keyword">defer</span> cancel()

    id := c.PostForm(<span class="hljs-string">"id"</span>)
    title := c.PostForm(<span class="hljs-string">"title"</span>)
    content := c.PostForm(<span class="hljs-string">"content"</span>)

    now := time.Now()

    <span class="hljs-keyword">if</span> id == <span class="hljs-string">""</span> {
        post := Post{
            Title:     title,
            Content:   content,
            CreatedAt: now,
            UpdatedAt: now,
        }

        result, err := h.collection.InsertOne(ctx, post)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            c.JSON(http.StatusInternalServerError, gin.H{<span class="hljs-string">"error"</span>: err.Error()})
            <span class="hljs-keyword">return</span>
        }

        log.Printf(<span class="hljs-string">"Created post with ID: %v\n"</span>, result.InsertedID)
    } <span class="hljs-keyword">else</span> {
        objID, err := primitive.ObjectIDFromHex(id)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            c.JSON(http.StatusBadRequest, gin.H{<span class="hljs-string">"error"</span>: <span class="hljs-string">"Invalid post ID"</span>})
            <span class="hljs-keyword">return</span>
        }

        update := bson.M{
            <span class="hljs-string">"$set"</span>: bson.M{
                <span class="hljs-string">"title"</span>:      title,
                <span class="hljs-string">"content"</span>:    content,
                <span class="hljs-string">"updated_at"</span>: now,
            },
        }

        result, err := h.collection.UpdateOne(ctx, bson.M{<span class="hljs-string">"_id"</span>: objID}, update)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            c.JSON(http.StatusInternalServerError, gin.H{<span class="hljs-string">"error"</span>: err.Error()})
            <span class="hljs-keyword">return</span>
        }

        log.Printf(<span class="hljs-string">"Updated post with ID: %s (Modified %d documents)\n"</span>, id, result.ModifiedCount)
    }

    c.Redirect(http.StatusSeeOther, <span class="hljs-string">"/"</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(h *Handler)</span> <span class="hljs-title">DeletePost</span><span class="hljs-params">(c *gin.Context)</span></span> {
    id := c.Param(<span class="hljs-string">"id"</span>)
    objID, err := primitive.ObjectIDFromHex(id)
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        c.JSON(http.StatusBadRequest, gin.H{<span class="hljs-string">"error"</span>: <span class="hljs-string">"Invalid post ID"</span>})
        <span class="hljs-keyword">return</span>
    }

    ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">5</span>*time.Second)
    <span class="hljs-keyword">defer</span> cancel()

    result, err := h.collection.DeleteOne(ctx, bson.M{<span class="hljs-string">"_id"</span>: objID})
    <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
        c.JSON(http.StatusInternalServerError, gin.H{<span class="hljs-string">"error"</span>: err.Error()})
        <span class="hljs-keyword">return</span>
    }

    log.Printf(<span class="hljs-string">"Deleted %d document(s) with ID: %s\n"</span>, result.DeletedCount, id)
    c.Redirect(http.StatusSeeOther, <span class="hljs-string">"/"</span>)
}
</code></pre>
<p>The code above contains all the logic for managing blog posts. Here's what each component does:</p>
<ul>
<li><p><strong>Post struct:</strong> Defines the structure of a blog post document with fields for ID, title, content, and timestamps. The <code>bson</code> tags specify how fields are stored in MongoDB, while <code>json</code> tags handle JSON serialization.</p>
</li>
<li><p><strong>Handler struct:</strong> Contains a reference to the MongoDB database and the posts collection, providing a centralized way to access the database throughout your handlers.</p>
</li>
<li><p><strong>NewHandler function</strong>: Creates and initializes a new handler instance with the database connection and sets up the posts collection reference.</p>
</li>
<li><p><strong>HomePage:</strong> Retrieves all blog posts from the database using <code>Find()</code> with an empty filter and renders them using the <code>index.html</code> template.</p>
</li>
<li><p><strong>ViewPost:</strong> Fetches a single post by its ObjectID using <code>FindOne()</code> and displays it with the <code>post.html</code> template.</p>
</li>
<li><p><strong>CreatePost &amp; EditPost:</strong> Render the respective forms for creating new posts or editing existing ones.</p>
</li>
<li><p><strong>SavePost:</strong> Handles both creating new posts and updating existing ones. It checks if an ID is provided. If not, it creates a new post using <code>InsertOne()</code>. Otherwise, it updates the existing post using <code>UpdateOne()</code> with MongoDB's <code>$set</code> operator.</p>
</li>
<li><p><strong>DeletePost:</strong> Removes a post from the database using <code>DeleteOne()</code> and redirects back to the homepage.</p>
</li>
</ul>
<h3 id="heading-run-the-application">Run the application</h3>
<p>With everything set up, you can now launch your blog. Open your terminal and run:</p>
<pre><code class="lang-bash">go mod tidy &amp;&amp; go run main.go
</code></pre>
<p>Then, visit <a target="_blank" href="http://localhost:8080">http://localhost:8080</a> in your browser to see your blog in action.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1753561817087/0e48d3d1-a6c0-4c5d-9245-cd6e86cd8596.png" alt="Blog management interface with a header &quot;Go Blog with MongoDB&quot; and a &quot;Create New Post&quot; button. Two blog post entries are shown with options to edit or delete." class="image--center mx-auto" width="512" height="283" loading="lazy"></p>
<h2 id="heading-thats-how-to-use-mongodb-with-go">That's How to Use MongoDB with Go</h2>
<p>In this tutorial, you built a simple blog application using Go and MongoDB. You learned how to connect to a MongoDB database using the official Go driver, perform CRUD operations, and render your results with the Gin web framework.</p>
<p>MongoDB’s flexible, document-based structure makes it a great fit for applications where data models need to evolve over time. It allows you to iterate quickly and adapt as your app grows.</p>
<p>As you expand this project, consider adding features such as user authentication, tagging or categorization, comments, pagination, or search functionality to enhance the user experience.</p>
<p>Cheers to building more with Go and MongoDB!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What Are JSON Web Tokens (JWT)? ]]>
                </title>
                <description>
                    <![CDATA[ When you’re working with any website, application, or API, you'll inevitably need to log in and authenticate your user base. One of the more commonly used methods of passing around authentication credentials from one system to another is using a JSON... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-are-json-web-tokens-jwt/</link>
                <guid isPermaLink="false">686c430b1a4ac707c0692874</guid>
                
                    <category>
                        <![CDATA[ JWT ]]>
                    </category>
                
                    <category>
                        <![CDATA[ authentication ]]>
                    </category>
                
                    <category>
                        <![CDATA[ authorization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Mon, 07 Jul 2025 21:58:35 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1751819356361/352ef68a-fa20-4a69-b666-393f7a17fa40.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you’re working with any website, application, or API, you'll inevitably need to log in and authenticate your user base. One of the more commonly used methods of passing around authentication credentials from one system to another is using a JSON Web Token (JWT).</p>
<p>In this article, you'll learn about:</p>
<ul>
<li><p>What a JSON Web Token (JWT) is</p>
</li>
<li><p>How JWTs are structured and created</p>
</li>
<li><p>Different JWT signing techniques and algorithms (Symmetric vs. Asymmetric)</p>
</li>
<li><p>How JWTs are used in real-world authentication flows</p>
</li>
<li><p>Important security best practices for using JWTs</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-a-jwt">What is a JWT?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-json-web-tokens-are-made-of-three-elements">JSON Web Tokens Are Made Of Three Elements</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-asymmetric-signing-rs256-explained">Asymmetric Signing (RS256) Explained</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-analogy-the-lock-and-key-for-asymmetric-signatures">Analogy: The Lock and Key for Asymmetric Signatures</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-symmetric-signing-hs256-hmac-with-sha-256">Symmetric Signing: HS256 (HMAC with SHA-256)</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-how-hs256-verification-works">How HS256 Verification Works</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-jwts-in-action-a-typical-authentication-flow">JWTs in Action: A Typical Authentication Flow</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-jwt-security-best-practices-amp-considerations">JWT Security Best Practices &amp; Considerations</a></p>
</li>
</ul>
<h2 id="heading-what-is-a-jwt">What Is a JWT?</h2>
<blockquote>
<p>JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.<br>— Introduction to JWT by jwt.io</p>
</blockquote>
<p>While accurate, this definition can be a bit dense at first glance. Imagine you want to send someone a sealed, tamper-proof message. That's essentially what a JSON Web Token (JWT) is. It's a secure message, a special kind of message designed to be sent between two parties which can be assured it came from an expected sender.</p>
<p>Each JWT is digitally signed using either a <strong>secret code</strong> (for symmetric algorithms like HMAC) or a <strong>private key</strong> (for asymmetric algorithms like RSA or ECDSA).</p>
<p>This secret code or private key is known only to the system that issues the JWT (often called an <em>authentication provider</em>, like Auth0, AWS Cognito, or Firebase Auth, which handles user logins and identity).</p>
<p>This signature proves two things:</p>
<ol>
<li><p><strong>Authenticity:</strong> It proves the message really came from who it claims to be from.</p>
</li>
<li><p><strong>Integrity:</strong> It proves that the message hasn't been changed or tampered with since it was signed. If even one character is altered, the signature won't match, and you'll know something is wrong, meaning the contents of the JWT can't be trusted.</p>
</li>
</ol>
<h3 id="heading-json-web-tokens-are-made-of-three-elements">JSON Web Tokens Are Made of Three Elements</h3>
<p>JWTs are made up of 3 key parts:</p>
<ol>
<li><p>Header</p>
</li>
<li><p>Payload</p>
</li>
<li><p>Signature</p>
</li>
</ol>
<h4 id="heading-header">Header</h4>
<p>The header contains metadata information about the token. Think of it like a label on a package – it tells you what’s inside and how it was prepared.</p>
<p>Typically, the header contains:</p>
<p><code>alg</code>: This specifies the <strong>algorithm</strong> used to sign the JWT. Common algorithms are <code>HS256</code> (HMAC with SHA-256) or <code>RS256</code> (RSA with SHA-256).</p>
<p><code>typ</code>: This specifies the <strong>type</strong> of token, which is almost always <code>JWT</code> for standard JSON Web Tokens.</p>
<p><strong>Example (decoded):</strong></p>
<pre><code class="lang-json">{ 
  <span class="hljs-attr">"alg"</span>: <span class="hljs-string">"RS256"</span>,
  <span class="hljs-attr">"typ"</span>: <span class="hljs-string">"JWT"</span> 
}
</code></pre>
<h4 id="heading-payload">Payload</h4>
<p>This is the second part of the JWT, and it's where the real data or "claims" are stored. Claims are statements about an entity (usually a user) and any other additional data. Using the previous analogy of a package, think of it as the “contents” of the package.</p>
<p>There are three types of claims:</p>
<p><strong>1. Registered Claims:</strong> These are predefined claims that are recommended for common use cases. They are not mandatory but are very useful for interoperability. These include:</p>
<ul>
<li><p><code>iss</code> – issuer, who issued the token (for example, your application’s domain)</p>
</li>
<li><p><code>sub</code> – subject, the subject of the token (for example, a User’s ID)</p>
</li>
<li><p><code>aud</code> – audience, the audience of the token (that is, who the token is intended for – for example, a specific API)</p>
</li>
<li><p><code>exp</code> – <strong>expiration</strong>, the expiry date as a timestamp</p>
</li>
<li><p><code>iat</code> – issued at, when the token was issued as a timestamp</p>
</li>
<li><p><code>nbf</code> – not before, when the token becomes valid (that is, the token cannot be used or deemed valid before this timestamp)</p>
</li>
<li><p><code>jti</code> – JWT ID, a unique identifier for the token, useful for preventing replay attacks or blacklisting</p>
</li>
</ul>
<p><strong>2. Public Claims:</strong> These can be defined by anyone using JWTs. To avoid naming conflicts, it's a good practice to register them or define them using a unique identifier like a URI.</p>
<p><strong>3. Private Claims:</strong> These are custom claims created to share specific information between parties who agree on using them. They are entirely up to you and your application's needs.</p>
<p><strong>Example payload:</strong></p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"sub"</span>: <span class="hljs-string">"1234567890"</span>, <span class="hljs-comment">//  subject</span>
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"John Doe"</span>, <span class="hljs-comment">// private claim</span>
  <span class="hljs-attr">"admin"</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// private claim / role</span>
  <span class="hljs-attr">"iat"</span>: <span class="hljs-number">1678886400</span>, <span class="hljs-comment">// Issued at a specific timestamp</span>
  <span class="hljs-attr">"exp"</span>: <span class="hljs-number">1678890000</span>  <span class="hljs-comment">// Expires at a specific timestamp</span>
}
</code></pre>
<p>Like the header, this JSON object is also <strong>Base64Url encoded</strong> (a URL-safe variant of Base64 encoding) to form the second part of the JWT string.</p>
<p><strong>Important Note:</strong> <em>The payload is</em> <strong><em>encoded*</em></strong>, not encrypted.* This means that anyone can easily decode the JWT and read its contents. Never put sensitive information (like passwords) directly into the payload unless the entire JWT itself is encrypted (which is a separate process called JWE - JSON Web Encryption). The security of a standard JWT comes entirely from the signature, which prevents tampering.</p>
<h4 id="heading-signature">Signature</h4>
<p>The signature, as we've already discussed, is the most important part of the JWT. Without it, there's no protection applied to the JWT, meaning no way to validate the origin of the token or its integrity.</p>
<p>The signature is created by taking the <strong>encoded header</strong>, the <strong>encoded payload</strong>, and a <strong>secret key</strong> (or a private key if using asymmetric algorithms like RSA). These are then run through the cryptographic algorithm specified in the header (<code>alg</code> field). For HS256, a shared secret key is used. For RS256, a private key is used to sign, and a corresponding public key is used to verify. We’ll get on to verification soon.</p>
<p>Think of it like a tamper-proof seal on your package, or even better, a wax seal on a letter. If you receive your letter and the wax seal has been broken, you'd naturally believe the contents of the letter may not be original and therefore can't be trusted.</p>
<p>In pseudo-code it would look like this:</p>
<p><code>Signature = Algorithm( Base64Url(Header) + "." + Base64Url(Payload), SecretKey )</code></p>
<p>The result of this signing process is the signature, which is also Base64Url encoded to form the third part of the JWT string.</p>
<p>At the end of the whole process your JWT would look like this:</p>
<p><code>base64EncodedHeader.base64EncodedPayload.base64EncodedSignature</code></p>
<h3 id="heading-asymmetric-signing-rs256-explained">Asymmetric Signing (RS256) Explained</h3>
<p>When a JWT uses an algorithm like RS256 (RSA Signature with SHA-256), it employs an <strong>asymmetric cryptographic</strong> process involving a <strong>public</strong> and <strong>private</strong> key pair. This is where the core magic of proving authenticity and integrity happens without needing to share a secret.</p>
<h4 id="heading-the-signing-process-by-the-issuer">The Signing Process (by the Issuer)</h4>
<p>The <strong>sender</strong> (the server that issues the JWT, like Auth0) possesses the <strong>private key</strong>. This key is kept absolutely secret and secure. Here are the steps:</p>
<ol>
<li><p><strong>Prepare the data:</strong> The server takes the header (which includes the algorithm) and the payload. It Base64Url-encodes them, and then concatenates them with a dot: <code>Base64Url(Header) + "." + Base64Url(Payload)</code>.</p>
<p> For example, with this header:</p>
<pre><code class="lang-json"> {
   <span class="hljs-attr">"typ"</span>: <span class="hljs-string">"JWT"</span>,
   <span class="hljs-attr">"alg"</span>: <span class="hljs-string">"RS256"</span>
 }
</code></pre>
<p> And this payload:</p>
<pre><code class="lang-json"> {
   <span class="hljs-attr">"sub"</span>: <span class="hljs-string">"1234567890"</span>,
   <span class="hljs-attr">"name"</span>: <span class="hljs-string">"John Doe"</span>,
   <span class="hljs-attr">"admin"</span>: <span class="hljs-literal">true</span>,
   <span class="hljs-attr">"iat"</span>: <span class="hljs-number">1751494086</span>,
   <span class="hljs-attr">"exp"</span>: <span class="hljs-number">1751497686</span>
 }
</code></pre>
<p> This would create a Base64Url-encoded <code>header.payload</code> string like the animated example below:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751810671075/128fa652-fe2a-4413-a238-71531bfe67ae.gif" alt="Animated gif of the process converting header and payload to base64 encoded string" class="image--center mx-auto" width="800" height="587" loading="lazy"></p>
</li>
<li><p><strong>Calculate the hash:</strong> It then calculates a <strong>hash</strong> (using SHA-256 in this case) of this combined header and payload string.</p>
</li>
<li><p><strong>Sign the hash:</strong> Finally, it signs this hash using its private key. This cryptographically transformed hash is the signature part of the JWT.</p>
</li>
</ol>
<p>The JWT is then formed by concatenating the Base64Url-encoded header, the Base64Url-encoded payload, and the Base64Url-encoded signature, separated by dots: <code>header.payload.signature</code>.</p>
<p>The top segment below shows the full JWT token (header.payload.signature):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751830473890/31da21a0-50db-4a48-b4c3-378c2ac1616a.png" alt="Image displaying the fully formed JWT token along with original header and payload" class="image--center mx-auto" width="2630" height="1100" loading="lazy"></p>
<h3 id="heading-the-verification-process">The Verification Process</h3>
<p>This is where the magic happens, and it's often a point of confusion. The public key doesn't "decrypt" the original data like a symmetric key does. Instead, it performs a unique <strong>verification</strong> process.</p>
<p>The receiver (the client or another server that needs to verify the JWT) possesses the <strong>public key</strong>. This key does <strong>not</strong> need to be kept secret – it can be freely distributed.</p>
<p>Here's a step-by-step explanation:</p>
<ol>
<li><p><strong>Separate the parts:</strong> The first thing the receiver does is split the incoming JWT string into its three Base64Url-encoded components: the Header, the Payload, and the Signature.</p>
</li>
<li><p><strong>Obtain the public key:</strong> The verifier needs the <strong>public key</strong> that corresponds to the private key used by the issuer. Public keys are often available via a <strong>JWKS (JSON Web Key Set) endpoint</strong> (for example, <code>your-domain.com/.well-known/jwks.json</code>).</p>
</li>
<li><p><strong>Re-create the data to be hashed:</strong> The receiver takes the received, Base64Url-encoded Header and the received, Base64Url-encoded Payload. It then combines them exactly as the issuer did: <code>EncodedHeader.EncodedPayload</code>.</p>
</li>
<li><p><strong>Compute a local hash (Hash A):</strong> This combined string is then put through the same hashing algorithm (for example, SHA-256) that was specified in the JWT's header. This produces a new, locally computed hash (let's call this <strong>"Hash A"</strong>). This local hash represents what the content <em>should</em> look like if it hasn't been tampered with.</p>
</li>
<li><p><strong>"Unsign" the received signature with the public key to get the original signed hash (Hash B):</strong> This is the core cryptographic step. The verifier uses the public key (obtained in step 2) to perform a mathematical operation on the received signature. This operation does <em>not</em> create a new signature for comparison. Instead, it effectively "unsigns" or "decrypts" the signature to reveal the <strong>original hash ("Hash B")</strong> that was produced by the issuer's private key.</p>
<ul>
<li><strong>Crucial Point:</strong> This process is for <strong>verifying authenticity</strong>, not decrypting confidential data. The public key confirms that the signature was indeed created by the corresponding private key, and as part of that confirmation, it returns the original hash that <em>was signed</em>.</li>
</ul>
</li>
<li><p><strong>Compare the hashes:</strong> The verifier now has two hashes:</p>
<ul>
<li><p><strong>Hash A:</strong> The hash it <strong>computed locally</strong> from the received header and payload (from step 4).</p>
</li>
<li><p><strong>Hash B:</strong> The original hash that was extracted from the received Signature using the public key (from step 5)</p>
</li>
</ul>
</li>
<li><p><strong>If Hash A matches Hash B:</strong> It proves two critical things:</p>
<ol>
<li><p><strong>Authenticity:</strong> The token was indeed signed by the legitimate holder of the corresponding private key (for example, Auth0).</p>
</li>
<li><p><strong>Integrity:</strong> The content of the header and payload has <strong>not been tampered with</strong> since it was originally signed. If even a single character in the header or payload were changed, Hash A would be different, and it would not match Hash B. In this case, the JWT is considered valid and its contents can be trusted.</p>
</li>
</ol>
</li>
<li><p><strong>If the hashes do NOT match:</strong> The token is considered invalid and <strong>must be rejected</strong>. This indicates either that the JWT was signed by an unauthorised party (a forged token) or that its header or payload has been altered after it was signed.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751813812291/de32c7b0-1f3c-4f26-995e-b5c618b104b3.png" alt=" Flow diagram of asymmetric verification process" class="image--center mx-auto" width="1649" height="2947" loading="lazy"></p>
<h3 id="heading-analogy-the-lock-and-key-for-asymmetric-signatures">Analogy: The Lock and Key for Asymmetric Signatures</h3>
<p><strong>Private Key:</strong> A special, unique key that can <strong>lock</strong> a box (create a signature). Only the owner has this key.</p>
<p><strong>Public Key:</strong> A widely distributed key that can <strong>test</strong> if a box was locked by the corresponding private key. It can't lock a new box, but it can confirm if an existing lock is authentic.</p>
<p>You don't re-lock the box with the public key. You use the public key to check if the existing lock (the signature) is genuine and corresponds to the contents of the box.</p>
<h2 id="heading-symmetric-signing-hs256-hmac-with-sha-256">Symmetric Signing: HS256 (HMAC With SHA-256)</h2>
<p>While RS256 uses a pair of keys (private for signing, public for verifying), many JWTs you'll encounter are signed symmetrically, most commonly with the HS256 algorithm. HS256 stands for <strong>HMAC (Hash-based Message Authentication Code) with SHA-256</strong>.</p>
<p>The fundamental difference here is the use of a single, shared secret key for <em>both</em> signing and verification.</p>
<h3 id="heading-how-hs256-signing-works">How HS256 Signing Works</h3>
<ol>
<li><p><strong>Shared secret key:</strong> The issuer (for example, your authentication provider) possesses a single, confidential secret key. This key is known <em>only</em> to the issuer and any parties (like your API) that need to verify the token.</p>
</li>
<li><p><strong>Combine header and payload:</strong> Just like with asymmetric signing, the issuer takes the Base64Url-encoded Header (which specifies <code>"alg": "HS256"</code>) and the Base64Url-encoded Payload, and <strong>joins</strong> them with a dot.</p>
</li>
<li><p><strong>Apply HMAC-SHA256:</strong> This combined string is then fed into the HMAC-SHA256 algorithm along with the secret key. The HMAC algorithm uses the secret key to create a unique hash (the signature) of the data. In pseudo-code, it looks like this:</p>
<p> <code>Signature = HMAC-SHA256( Base64Url(Header) + "." + Base64Url(Payload), SecretKey )</code></p>
</li>
<li><p><strong>Form the JWT:</strong> The resulting signature (which is also Base64Url-encoded) is appended to the header and payload with a dot, forming the complete JWT: <code>base64EncodedHeader.base64EncodedPayload.base64EncodedSignature</code>.</p>
</li>
</ol>
<h3 id="heading-how-hs256-verification-works">How HS256 Verification Works</h3>
<p>When a receiver gets an HS256-signed JWT, it goes through a verification process.</p>
<p>First, it separates the parts. The JWT is split into its three Base64Url-encoded components: Header, Payload, and Signature, as we did with asymmetric JWTs.</p>
<p>Then, it obtains the shared secret key. The receiver must also possess the <strong>exact same secret key</strong> that the issuer used to sign the token. This key is <em>not</em> publicly distributed like a public key – it must be securely provisioned to any entity that needs to verify tokens.</p>
<p>Next, it re-calculates the signature. The receiver does this by taking the received Base64Url-encoded Header and Payload, combining them, and then re-applying the HMAC-SHA256 algorithm using the <em>same secret key</em>. This produces a new, locally computed signature.</p>
<p>Finally, the receiver compares the signature it just calculated locally with the signature it received as part of the JWT.</p>
<ul>
<li><p><strong>If the two signatures match:</strong> The token is considered valid. This confirms its authenticity (it came from someone who knows the secret) and integrity (it hasn't been tampered with).</p>
</li>
<li><p><strong>If the signatures do NOT match:</strong> The token is invalid and must be rejected. This indicates either tampering or that it was signed with a different, unknown secret key.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751814851136/a73b7af6-e92d-40f3-b1e3-c4bd2406ede9.png" alt="Flow diagram of symmetric verification process" class="image--center mx-auto" width="1715" height="2977" loading="lazy"></p>
<h3 id="heading-key-differences-and-considerations">Key Differences and Considerations:</h3>
<ul>
<li><p><strong>Key management:</strong> With HS256, the secret key must be securely shared and kept confidential by <em>all</em> parties involved in both signing and verifying. This can be more challenging to manage securely at scale compared to the public/private key model, where only the private key needs strict secrecy.</p>
</li>
<li><p><strong>Performance:</strong> HS256 is generally faster to compute than asymmetric algorithms like RS256, making it suitable for high-volume scenarios where the secret key can be securely distributed.</p>
</li>
</ul>
<h2 id="heading-jwts-in-action-a-typical-authentication-flow">JWTs in Action: A Typical Authentication Flow</h2>
<p>Now that you understand how JWTs are structured and signed, let's look at how they're typically used in a real-world web application. This authentication flow is a common pattern you'd encounter.</p>
<h3 id="heading-step-1-user-logs-in"><strong>Step 1: User Logs In:</strong></h3>
<p>A user opens a client application (for example, a web browser, mobile app) and enters their login credentials (username and password).</p>
<p>The client sends these credentials securely (always over HTTPS!) to an <strong>authentication server</strong> (like Auth0, AWS Cognito, or your own backend's authentication endpoint).</p>
<h3 id="heading-step-2-authentication-server-issues-jwt"><strong>Step 2: Authentication Server Issues JWT:</strong></h3>
<p>Then the authentication server verifies the user's credentials. If valid, it generates a new JWT. This JWT contains claims (like the user's ID, roles, expiration time) in its payload and is digitally signed by the server's <strong>private key</strong> (for asymmetric algorithms like RS256) or <strong>secret key</strong> (for symmetric algorithms like HS256).</p>
<p>The server then sends this signed JWT back to the client.</p>
<h3 id="heading-step-3-client-stores-jwt"><strong>Step 3: Client Stores JWT:</strong></h3>
<p>The client receives the JWT and typically stores it in a secure location, such as browser memory storage, session storage, or an HTTP-only cookie. The method of storage depends on the client type and security considerations.</p>
<h3 id="heading-step-4-client-makes-api-calls"><strong>Step 4: Client Makes API Calls:</strong></h3>
<p>When the user wants to access a protected resource on a backend API (for example, their profile data, a private feed), the client includes the JWT in the request.</p>
<p>The standard way to do this is by sending the token in the <code>Authorization</code> header of the HTTP request, prefixed with the word <code>Bearer</code>:</p>
<p><code>Authorization: Bearer &lt;your_jwt_here&gt;</code></p>
<h3 id="heading-step-5-api-verifies-jwt-amp-authorises-request"><strong>Step 5: API Verifies JWT &amp; Authorises Request:</strong></h3>
<p>Now, the backend API receives the request and extracts the JWT from the <code>Authorization</code> header. The API then performs the JWT verification process depending on the algorithm:</p>
<ul>
<li><p>It checks the token's claims, especially the <code>exp</code> (expiration) claim, to ensure it's still valid.</p>
</li>
<li><p>If the token is valid, the API trusts the claims within the payload (for example, the user's ID) and proceeds to fulfill the request, potentially using the user's roles to determine if they have permission to access the requested resource.</p>
</li>
<li><p>If the token is invalid (bad signature, expired, and so on), the API rejects the request, typically with an HTTP 401 Unauthorised status.</p>
</li>
</ul>
<p>This flow is powerful because JWTs are <strong>stateless</strong>: once issued, the authentication server doesn't need to keep a record of active sessions. The API can verify the token independently, which simplifies scaling and reduces server load.</p>
<h2 id="heading-jwt-security-best-practices-and-considerations">JWT Security Best Practices and Considerations</h2>
<p>While JWTs offer powerful authentication capabilities, using them securely requires careful attention to best practices. Misconfigurations or oversight can lead to significant vulnerabilities.</p>
<h3 id="heading-always-use-httpstls"><strong>Always Use HTTPS/TLS:</strong></h3>
<p><strong>Crucial:</strong> JWTs are <strong>encoded, not encrypted, by default</strong>. This means anyone who intercepts the token during transmission can easily read its payload. Therefore, JWTs (and all authentication traffic) <strong>must always be transmitted over HTTPS (TLS)</strong> to encrypt the communication channel itself and prevent eavesdropping.</p>
<h3 id="heading-protect-your-signing-keys"><strong>Protect Your Signing Keys:</strong></h3>
<p>Whether it's a private key (for RS256) or a shared secret key (for HS256), these keys are paramount. If an attacker gains access to your signing key, they can forge valid JWTs, impersonate users, and compromise your system. Store these keys securely, preferably in dedicated key management services.</p>
<h3 id="heading-keep-access-tokens-short-lived-exp-claim"><strong>Keep Access Tokens Short-Lived (</strong><code>exp</code> claim):</h3>
<p>You should always set short expiration times (for example, 5-15 minutes) for your JWTs used as access tokens. This minimises the window of opportunity for an attacker if a token is compromised.</p>
<p>Since JWTs are stateless, they are hard to revoke immediately once issued. A short lifespan is your primary defense against compromised tokens.</p>
<h3 id="heading-implement-refresh-tokens-for-longer-sessions"><strong>Implement Refresh Tokens (for Longer Sessions):</strong></h3>
<p>To maintain user experience with short-lived access tokens, use <strong>refresh tokens</strong>. A refresh token is a separate, longer-lived token (usually stored more securely) that can be exchanged for a new, short-lived access token when the current one expires, without requiring the user to re-authenticate. Refresh tokens <em>can</em> be revoked by the server, offering better control.</p>
<h3 id="heading-never-put-sensitive-data-in-the-payload"><strong>Never Put Sensitive Data in the Payload:</strong></h3>
<p>Reiterating this crucial point: the JWT payload is Base64Url encoded, which is easily reversible. Do not put passwords, highly sensitive PII (Personally Identifiable Information), or confidential business data directly into the JWT payload. Only include non-sensitive or publicly available information, or data that's already encrypted by other means.</p>
<h3 id="heading-validate-all-claims-on-verification"><strong>Validate ALL Claims on Verification:</strong></h3>
<p>When verifying a JWT, don't just check the signature. Always validate all relevant claims, including:</p>
<ul>
<li><p><code>exp</code> (Expiration): Ensure the token hasn't expired.</p>
</li>
<li><p><code>iss</code> (Issuer): Verify the token came from the expected authentication server.</p>
</li>
<li><p><code>aud</code> (Audience): Ensure the token is intended for your specific API/application.</p>
</li>
<li><p><code>nbf</code> (Not Before): Check if the token is active yet.</p>
</li>
</ul>
<h3 id="heading-consider-token-revocation-for-critical-cases"><strong>Consider Token Revocation (for critical cases):</strong></h3>
<p>For situations requiring immediate revocation (for example, user password change, account deactivation), typical stateless JWTs are challenging. Strategies include:</p>
<ul>
<li><p>Short expiration times (as above).</p>
</li>
<li><p>A blacklist/revocation list: Store the <code>jti</code> (JWT ID) of revoked tokens in a database, checking this list on every request. This adds a stateful lookup but provides immediate revocation.</p>
</li>
</ul>
<h2 id="heading-thanks-for-reading">Thanks for reading!</h2>
<p>I hope you’ve found this tutorial useful, and as always if you want to ask any questions or hear about upcoming articles, you can always follow me on ‘X’, my handle is @grantdotdev and follow by clicking <a target="_blank" href="https://x.com/grantdotdev">here</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create Accessible and User-Friendly Forms in React ]]>
                </title>
                <description>
                    <![CDATA[ When designing web applications, you’ll often be asked the age old question “How accessible is your website” and “Does it offer the best user experience?”. These are both very valid questions, but they are often overlooked in favour of rich or fancy ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-accessible-and-user-friendly-forms-in-react/</link>
                <guid isPermaLink="false">6810f59e8deee87383c1bc46</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Accessibility ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Tue, 29 Apr 2025 15:51:58 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745789677789/c386af23-39d6-4421-9f26-f98d75a30d61.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When designing web applications, you’ll often be asked the age old question “How accessible is your website” and “Does it offer the best user experience?”. These are both very valid questions, but they are often overlooked in favour of rich or fancy looking features, reducing the site’s audience.</p>
<p>In this article, I’ll teach you about the React Hook Form library, HTML attributes, and development considerations to make sure your site’s available for all, focusing on:</p>
<ul>
<li><p>blind or visually impaired users, who may use a screen reader</p>
</li>
<li><p>better user feedback</p>
</li>
<li><p>visual queues for all</p>
</li>
<li><p>design considerations for all</p>
</li>
</ul>
<p>Whilst following along with this tutorial, you can either pull down the code from the GitHub repo (by visiting this <a target="_blank" href="https://github.com/grant-dot-dev/form_accessibility_ux">page</a>), or you can use the inline code snippets within the article.</p>
<h3 id="heading-pre-requisites-for-this-article"><strong>Pre-requisites for this article:</strong></h3>
<ul>
<li><p>Knowledge of React</p>
</li>
<li><p>Knowledge of writing TypeScript and HTML / JSX.</p>
</li>
<li><p>Familiarity with Tailwind CSS (not required in order to follow this tutorial)</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-the-initial-basic-form">The Initial Basic Form</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-error-handling-with-react-hook-form">Error Handling With React-Hook-Form</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-hooking-up-the-useform-methods-to-our-form">Hooking Up The useForm Methods To Our Form</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-showing-error-messages">Showing Error Messages</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-adding-aria-required">Adding aria-required</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-adding-fieldset-and-legend">Adding fieldset and legend</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-adding-labels-and-using-htmlfor">Adding Labels and Using htmlFor</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-do-not-rely-on-placeholders-only">Do Not Rely on Placeholders Only!</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-give-additional-information-with-aria-describedby">Give Additional Information With aria-describedBy</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-avoid-tooltips-for-critical-information">Avoid Tooltips for Critical Information</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tell-me-something-important">Tell Me Something Important</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-focus-states-and-colouring">Focus States and Colouring</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-make-buttons-descriptive">Make Buttons Descriptive</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-the-initial-basic-form">The Initial Basic Form</h2>
<p>So if we take a look at the form in its current state, you may think it looks fine. But it’s actually not very accessible, nor does it offer a great user experience.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { TvIcon } <span class="hljs-keyword">from</span> <span class="hljs-string">"@heroicons/react/24/outline"</span>;

<span class="hljs-keyword">type</span> FormData = {
    fullName: <span class="hljs-built_in">string</span>;
    email: <span class="hljs-built_in">string</span>;
    password: <span class="hljs-built_in">string</span>;
    confirmPassword: <span class="hljs-built_in">string</span>;
    agreeToTerms: <span class="hljs-built_in">boolean</span>;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> RegistrationForm = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> onSubmit = <span class="hljs-function">() =&gt;</span> {
        alert(<span class="hljs-string">`Form submitted`</span>);
    };

    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">"flex justify-center items-center w-screen h-screen bg-gray-900"</span>&gt;
            &lt;div className=<span class="hljs-string">"w-full max-w-md p-8 bg-black bg-opacity-75 rounded-lg"</span>&gt;
                &lt;div className=<span class="hljs-string">"flex flex-row justify-center items-center gap-x-4"</span>&gt;
                    &lt;TvIcon className=<span class="hljs-string">"h-12 w-12 text-white"</span> /&gt;
                    &lt;h1 className=<span class="hljs-string">"text-7xl font-bold text-center text-red-600 mb-4"</span>&gt;Getflix&lt;/h1&gt;
                &lt;/div&gt;
                &lt;h2 className=<span class="hljs-string">"text-3xl font-bold text-white mb-6 text-center"</span>&gt;
                    Sign Up
                &lt;/h2&gt;

                &lt;form onSubmit={onSubmit} className=<span class="hljs-string">"space-y-6"</span>&gt;

                    {<span class="hljs-comment">/* Full Name */</span>}
                    &lt;div&gt;
                        &lt;input
                            <span class="hljs-keyword">type</span>=<span class="hljs-string">"text"</span>
                            placeholder=<span class="hljs-string">"Full Name"</span>
                            className=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 "</span>
                        /&gt;

                    &lt;/div&gt;

                    {<span class="hljs-comment">/* Email */</span>}
                    &lt;div&gt;
                        &lt;input
                            <span class="hljs-keyword">type</span>=<span class="hljs-string">"email"</span>
                            placeholder=<span class="hljs-string">"Email Address"</span>
                            className=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 "</span>
                        /&gt;

                    &lt;/div&gt;

                    {<span class="hljs-comment">/* Password */</span>}
                    &lt;div&gt;
                        &lt;input
                            <span class="hljs-keyword">type</span>=<span class="hljs-string">"password"</span>
                            placeholder=<span class="hljs-string">"Password"</span>
                            className=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400"</span>
                        /&gt;

                    &lt;/div&gt;

                    {<span class="hljs-comment">/* Confirm Password */</span>}
                    &lt;div&gt;
                        &lt;input
                            <span class="hljs-keyword">type</span>=<span class="hljs-string">"password"</span>
                            placeholder=<span class="hljs-string">"Confirm Password"</span>
                            className=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 "</span>
                        /&gt;

                    &lt;/div&gt;

                    {<span class="hljs-comment">/* Agree to Terms */</span>}
                    &lt;div className=<span class="hljs-string">"flex items-center text-gray-400 text-sm"</span>&gt;
                        &lt;input

                            <span class="hljs-keyword">type</span>=<span class="hljs-string">"checkbox"</span>
                            id=<span class="hljs-string">"agreeToTerms"</span>
                            className=<span class="hljs-string">"mr-2"</span>
                        /&gt;
                        &lt;label htmlFor=<span class="hljs-string">"agreeToTerms"</span> className=<span class="hljs-string">"select-none"</span>&gt;
                            I agree to the Terms and Conditions
                        &lt;/label&gt;
                    &lt;/div&gt;


                    {<span class="hljs-comment">/* Submit */</span>}
                    &lt;button
                        <span class="hljs-keyword">type</span>=<span class="hljs-string">"submit"</span>
                        className=<span class="hljs-string">"w-full py-3 bg-red-600 hover:bg-red-700 text-white rounded font-semibold transition"</span>
                    &gt;
                        Sign Up
                    &lt;/button&gt;


                &lt;/form&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );
};
</code></pre>
<h3 id="heading-whats-wrong-with-the-form">What’s Wrong With The Form?</h3>
<ul>
<li><p>Lack of action feedback – no user feedback means that users can become confused as to whether an action has happened or not. No error messages or feedback offers the user no insight into what they need to do to correct the form.</p>
</li>
<li><p>No labels for form inputs – No labels for form inputs prevent screen readers from understanding their purpose. Some screen readers may miss placeholders, and once a user types within the input, the placeholder is replaced, losing context and making it hard to return to erroneous inputs.</p>
</li>
<li><p>Lack of accessibility markup to make the form optimised for screen readers and accessibility tools.</p>
</li>
</ul>
<p>So how do we make this better? Let’s jump right in.</p>
<h2 id="heading-error-handling-with-react-hook-form">Error Handling With React-Hook-Form</h2>
<p>Error handling on forms is a critical aspect of any form submission flow. Without it, the process becomes both chaotic and frustrating for the user. We can alleviate this frustration by adding some useful error messages which explain the issues.</p>
<p>A popular library for working with forms in React is the <code>react-hook-form</code> library. It’s used by over 1.4 million people according to their GitHub statistics.</p>
<p>Go ahead and install it if you don’t have it already:</p>
<pre><code class="lang-bash">npm install react-hook-form
</code></pre>
<p>We will then implement the basic required functions from the <code>react-hook-form</code> package, using the <code>useForm()</code> hook like so:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// define our type structure to use within the form</span>
<span class="hljs-keyword">type</span> FormData = {
    fullName: <span class="hljs-built_in">string</span>;
    email: <span class="hljs-built_in">string</span>;
    password: <span class="hljs-built_in">string</span>;
    confirmPassword: <span class="hljs-built_in">string</span>;
    agreeToTerms: <span class="hljs-built_in">boolean</span>;
};

<span class="hljs-comment">// basic usage of `useForm()`</span>
<span class="hljs-keyword">const</span> {
    register,
    handleSubmit,
    watch,
    formState: { errors },
  } = useForm&lt;Inputs&gt;()
</code></pre>
<p><strong>Quick Explanation:</strong></p>
<ul>
<li><p><code>register</code>: One of the key concepts in React Hook Form is to “register” your component / HTML element. This means you can access value of the element for both form validation and when submitting the form.</p>
</li>
<li><p><code>handleSubmit</code>: This is the key function needed to submit the form, run validation, and any other configured checks. It can take up to two arguments:</p>
<ol>
<li><p><code>handleSubmit(onSuccess)</code> – called when the submission of the form is valid and can submit ok.</p>
</li>
<li><p><code>handleSubmit(onSuccess, onFail)</code> – here you can pass the <code>handleSubmit()</code> method two functions: the first will be run when React Hook Form deems the form to be valid, and allows you to continue. The second will be called when the form sees an error. This could be from validation, or another stipulation.</p>
</li>
</ol>
</li>
<li><p><code>watch</code>: Watch is a function that monitors a specified element for changes and returns its value. For instance, if you’re watching an input element, you can output the user’s typing in real-time or have another element validate it against a predefined value. A good example is a confirm password matching the previous password field.</p>
</li>
<li><p><code>formState</code>: this is an object which holds information about your form. The <code>formState</code> object keeps track of the state of the form, like:</p>
<ol>
<li><p><strong>isDirty</strong> – <code>true</code> if the user has changed <em>any</em> input.</p>
</li>
<li><p><strong>isValid</strong> – <code>true</code> if the form passes all validations.</p>
</li>
<li><p><strong>errors</strong> – an object holding any validation errors per field.</p>
</li>
<li><p><strong>isSubmitting</strong> – <code>true</code> while the form is being submitted (useful for showing loading spinners)</p>
</li>
<li><p><strong>isSubmitted</strong> – <code>true</code> after the form has been submitted.</p>
</li>
<li><p><strong>touchedFields</strong> – which fields the user has interacted with.</p>
</li>
<li><p><strong>dirtyFields</strong> – which fields the user has modified.</p>
</li>
</ol>
</li>
</ul>
<p>We can use any of these properties by including them in our form state object. We are destructing the <code>errors</code> property so we can use the errors later in our form to either show error messages, or validate that there no errors on the page.</p>
<h2 id="heading-hooking-up-the-useform-methods-to-our-form">Hooking Up the <code>useForm</code> Methods to Our Form</h2>
<p>Now that we know more about the <code>useForm()</code> method and react-hook-form, we need to integrate this with our existing <code>&lt;form/&gt;</code> element. Doing so will allow us to use all the react-hook-form features we’ve discussed so far in our form.</p>
<pre><code class="lang-xml">import { TvIcon } from "@heroicons/react/24/outline";
import { useState } from "react";
import { useForm } from "react-hook-form";

type FormData = {
    fullName: string;
    email: string;
    password: string;
    confirmPassword: string;
    agreeToTerms: boolean;
};

export const RegistrationForm = () =&gt; {
    const {
        register,
        handleSubmit,
        formState: { errors },
        watch,
    } = useForm<span class="hljs-tag">&lt;<span class="hljs-name">FormData</span>&gt;</span>();

    const onSubmit = () =&gt; {
        alert(`Form submitted`);
    };

    return (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-center items-center w-screen h-screen bg-gray-900"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full max-w-md p-8 bg-black bg-opacity-75 rounded-lg"</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-row justify-center items-center gap-x-4"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">TvIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-12 w-12 text-red-500"</span> /&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-7xl font-bold text-center text-white mb-4"</span>&gt;</span>Getflix<span class="hljs-tag">&lt;/<span class="hljs-name">h1</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">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl font-bold text-white mb-6 text-center"</span>&gt;</span>
                    Sign Up
                <span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>


                <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleSubmit(onSubmit)}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"space-y-6"</span>&gt;</span>

                    {/* Full Name */}
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                            {<span class="hljs-attr">...register</span>("<span class="hljs-attr">fullName</span>", {
                                <span class="hljs-attr">required:</span> "<span class="hljs-attr">Full</span> <span class="hljs-attr">Name</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>"
                            })}
                            <span class="hljs-attr">aria-required</span>
                            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
                            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Full name"</span>
                            <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"</span>
                        /&gt;</span>
                        {errors.fullName &amp;&amp; (
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 text-sm mt-1"</span>&gt;</span>{errors.fullName.message}<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>

                    {/* Email */}
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                            {<span class="hljs-attr">...register</span>("<span class="hljs-attr">email</span>", {
                                <span class="hljs-attr">required:</span> "<span class="hljs-attr">Email</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>",
                                <span class="hljs-attr">pattern:</span> {
                                    <span class="hljs-attr">value:</span> /^\<span class="hljs-attr">S</span>+@\<span class="hljs-attr">S</span>+$/<span class="hljs-attr">i</span>,
                                    <span class="hljs-attr">message:</span> "<span class="hljs-attr">Invalid</span> <span class="hljs-attr">email</span> <span class="hljs-attr">address</span>",
                                },
                            })}
                            <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
                            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Email Address"</span>
                            <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"</span>
                        /&gt;</span>
                        {errors.email &amp;&amp; (
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 text-sm mt-1"</span>&gt;</span>{errors.email.message}<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>

                    {/* Password */}
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                            {<span class="hljs-attr">...register</span>("<span class="hljs-attr">password</span>", {
                                <span class="hljs-attr">required:</span> "<span class="hljs-attr">Please</span> <span class="hljs-attr">enter</span> <span class="hljs-attr">your</span> <span class="hljs-attr">password</span>",
                            })}
                            <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span>
                            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Password"</span>
                            <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"</span>
                        /&gt;</span>
                        {errors.password &amp;&amp; (
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 text-sm mt-1"</span>&gt;</span>{errors.password.message}<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>

                    {/* Confirm Password */}
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                            {<span class="hljs-attr">...register</span>("<span class="hljs-attr">confirmPassword</span>", {
                                <span class="hljs-attr">required:</span> "<span class="hljs-attr">Please</span> <span class="hljs-attr">enter</span> <span class="hljs-attr">your</span> <span class="hljs-attr">password</span>",
                                <span class="hljs-attr">validate:</span> (<span class="hljs-attr">value</span>) =&gt;</span>
                                    value === watch("password") || "Passwords do not match",
                            })}
                            type="password"
                            placeholder="Confirm Password"
                            className="w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"
                        /&gt;
                        {errors.confirmPassword &amp;&amp; (
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 text-sm mt-1"</span>&gt;</span>{errors.confirmPassword.message}<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>

                    {/* Agree to Terms */}
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center text-gray-400 text-sm"</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                            {<span class="hljs-attr">...register</span>("<span class="hljs-attr">agreeToTerms</span>", {
                                <span class="hljs-attr">required:</span> "<span class="hljs-attr">You</span> <span class="hljs-attr">must</span> <span class="hljs-attr">agree</span> <span class="hljs-attr">to</span> <span class="hljs-attr">the</span> <span class="hljs-attr">terms</span> <span class="hljs-attr">and</span> <span class="hljs-attr">conditions</span>"
                            })}
                            <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span>
                            <span class="hljs-attr">id</span>=<span class="hljs-string">"agreeToTerms"</span>
                            <span class="hljs-attr">className</span>=<span class="hljs-string">"mr-2"</span>
                        /&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"select-none"</span>&gt;</span>
                            I agree to the Terms and Conditions
                        <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>
                    {errors.agreeToTerms &amp;&amp; (
                        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 text-sm mt-1"</span>&gt;</span>{errors.agreeToTerms.message}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                    )}


                    {/* Submit */}
                    <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">className</span>=<span class="hljs-string">"w-full py-3 bg-red-600 hover:bg-red-700 text-white rounded font-semibold transition"</span>
                    &gt;</span>
                        Sign Up
                    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

                    {/* Already have account */}
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-center text-gray-400 text-sm mt-4"</span>&gt;</span>
                        Already have an account?{" "}
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 hover:underline"</span>&gt;</span>
                            Sign In
                        <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">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span> &gt;</span>
    );
};
</code></pre>
<p>So in the updated form code, we’ve made a few adjustments:</p>
<h3 id="heading-registered-each-our-elements">Registered Each Our Elements</h3>
<p>For each of our elements we’ve added the <code>register</code> object, and configuring some overrides.</p>
<p>We added the <strong>required</strong> property to all input fields, which checks if the element has a value. If not, it records the provided name and marks the error as erroneous, updating the errors object with our name and the provided <em>required</em> message.</p>
<pre><code class="lang-typescript"> {...register(<span class="hljs-string">"fullName"</span>, {
    required: <span class="hljs-string">"Full Name is required"</span>
  })}
</code></pre>
<p>We’ve added a <code>pattern</code> property on the email’s register object. This allows us to specify a criteria for the value of the input – perfect for passwords, email fields, and other inputs which may have value restrictions, or requirements.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// valid email pattern</span>
pattern: {
    value: <span class="hljs-regexp">/^\S+@\S+$/i</span>,
    message: <span class="hljs-string">"Invalid email address"</span>,
},
</code></pre>
<p>We have also added the <code>validate</code> property to the confirm password element. This is a given function that will run as the user types.</p>
<pre><code class="lang-typescript">validate: <span class="hljs-function">(<span class="hljs-params">value</span>) =&gt;</span> value === watch(<span class="hljs-string">"password"</span>) || <span class="hljs-string">"Passwords do not match"</span>
</code></pre>
<p>The <code>validate</code> function inside <code>register</code> is run <strong>automatically</strong> based on the field's <code>validationMode</code> setting.</p>
<p>By default (if you do not specify the <code>validationMode</code>), React Hook Form runs validation on <code>onChange</code> and <code>onBlur</code> events. This means that:</p>
<ul>
<li><p>When the user types into the input → it triggers <code>validate</code>.</p>
</li>
<li><p>When the user leaves (blurs) the input → it triggers <code>validate</code> again.</p>
</li>
</ul>
<p>If you wanted to update the custom validation mode, you can override this using the <code>mode</code> setting within <code>useForm()</code> like so:</p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">const</span> { register, handleSubmit, formState, trigger } = useForm({
    mode: <span class="hljs-string">"onSubmit"</span>,
  });
</code></pre>
<p>If you then want to go an extra step and update the mode per element, overriding the <code>mode</code> setting you just globally set for your form, you can use the <code>trigger()</code> method from <code>useForm</code> like so:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span>
  {<span class="hljs-attr">...register</span>("<span class="hljs-attr">email</span>", { <span class="hljs-attr">required:</span> "<span class="hljs-attr">Email</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>" })}
  <span class="hljs-attr">onBlur</span>=<span class="hljs-string">{()</span> =&gt;</span> trigger("email")} // validate this field onBlur manually
/&gt;
</code></pre>
<p>This allows you to have <code>onSubmit</code> validation set via <code>mode</code>, and then email is triggered via <code>onBlur()</code> too.</p>
<p>Just adding these simple settings within the react-hook-form library already gives us a much better user experience than before – but it isn’t everything. Let’s explore more settings, HTML, and attributes we can add to increase accessibility and user experience.</p>
<h2 id="heading-showing-error-messages">Showing Error Messages</h2>
<p>Form errors can be stored within the <code>formState</code> object we mentioned earlier, but they’re no good there – we need to display them to our users. We can achieve this simply by accessing the destructed <code>errors</code> object, like below:</p>
<pre><code class="lang-xml">{errors.password &amp;&amp; (
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 text-sm mt-1"</span>&gt;</span>{errors.password.message}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
)}
</code></pre>
<p>The code uses conditional syntax to show the <code>&lt;p&gt;</code> tag only if the <code>errors.password</code> object has a value, indicating an error associated with the password field from <code>useForm()</code> checks. We can then display the error message from <code>errors.password.message</code>, combined with a commonly used erroneous colour like red, to highlight the form’s problems. This can then been applied to all other input fields as per the code above.</p>
<h2 id="heading-adding-aria-required">Adding <code>aria-required</code></h2>
<p>So we’ve informed the form that certain elements are required and these should be checked when submitting the form. But this alone doesn’t inform visually impaired users that the element is required.</p>
<p>To aid with screen-readers, we can add an <code>aria</code> attribute to our element which will be read by the screen-reader. This property is the <code>aria-required</code> property. This means that when the screen-reader reads out information about the element it will inform the user that this value is required for successful submission.</p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
    {<span class="hljs-attr">...register</span>("<span class="hljs-attr">fullName</span>", {
        <span class="hljs-attr">required:</span> "<span class="hljs-attr">Full</span> <span class="hljs-attr">Name</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>"
    })}
    <span class="hljs-attr">aria-required</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Full name"</span>
    <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"</span>
/&gt;</span>
</code></pre>
<h2 id="heading-adding-fieldset-and-legend">Adding <code>fieldset</code> and <code>legend</code></h2>
<p>Fieldset elements group <code>&lt;form&gt;</code> controls together, while legend elements provide a description for the grouped controls.</p>
<p>Imagine you have one big form, but it spans two "sections" – for example, a "<em>User Details</em>" section for username, email, and passwords, and an "<em>Address Details</em>" section asking for your shipping and billing information.</p>
<p>In this tutorial, we’re using TailwindCSS, which provides a utility class called <code>sr-only</code>. You can apply <code>sr-only</code> to your legends so they are only visible to screen readers, and not actually visible on the page.</p>
<p>This way, the legend will be read aloud when users navigate into a section of the form, making it clear which part of the form they are interacting with.</p>
<p><strong>Important Note:</strong> Legends must be placed inside fieldsets. You need to wrap your legends within a <code>&lt;fieldset&gt;</code> element for your HTML to be valid and accessible.</p>
<p>Here's an unrelated example (to keep it brief and simple):</p>
<pre><code class="lang-xml">
  <span class="hljs-tag">&lt;<span class="hljs-name">fieldset</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">legend</span>&gt;</span>Payment Method<span class="hljs-tag">&lt;/<span class="hljs-name">legend</span>&gt;</span>    
    <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">"radio"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"payment"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"card"</span> /&gt;</span>
      Credit Card
    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>

    <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">"radio"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"payment"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"paypal"</span> /&gt;</span>
      PayPal
    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">fieldset</span>&gt;</span>
</code></pre>
<p>You can see that the payment option inputs have been grouped within a fieldset, and then described by the <code>legend</code> element, informing the user that these elements relate to “<em>Payment Method</em>”. You as the developer can then decide if you would like this shown to everyone, or if it’s only for visually impaired users.</p>
<p>For screen readers, they’d hear something like:</p>
<blockquote>
<p>"Group: Payment Method. Credit Card radio button. PayPal radio button."</p>
</blockquote>
<h2 id="heading-do-not-rely-on-placeholders-only">Do Not Rely on Placeholders Only!</h2>
<p>Placeholders are a great addition to make it clear to the user what the input elements are used for, and show helpful information. But they aren’t that user friendly, especially in regards to screen-readers.</p>
<p>The main reasons for this are:</p>
<ul>
<li><p>Placeholders disappear when typing, meaning that if a user begins to type “<em>Grant</em>”, and then tabs away from the input when they go back, without a label it will simply read the value of the input, not what it relates to.</p>
</li>
<li><p>Often developers utilise a grey-like colour for their placeholders, with a low opacity. This can mean it’s difficult for users to sometimes see the placeholder, especially those who are colour blind or visually impaired.</p>
</li>
</ul>
<p>So what can we do instead ? Well this leads me onto our next point – we can use a common HTML element, the <code>&lt;label/&gt;</code>.</p>
<h2 id="heading-adding-labels-and-using-htmlfor">Adding Labels and Using <code>htmlFor</code></h2>
<p>Another accessibility feature we can add to boost our accessibility and user experience for all, is the <code>htmlFor</code> attribute combined with the <code>&lt;label/&gt;</code> element.</p>
<p>Labels are highly important for both sighted and visually impaired users. It offers clarity as to what the input is associated with, as well as a navigational tool for those using screen-readers.</p>
<p>The <code>htmlFor</code> attribute is used to link <code>&lt;label/&gt;</code> elements with their input.</p>
<p><strong>Note:</strong> <code>htmlFor</code> <em>attributes can only be used on labels and are not valid on any other element.</em></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"fullname"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-white"</span>&gt;</span>Full Name<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">...register</span>("<span class="hljs-attr">fullName</span>", {
        <span class="hljs-attr">required:</span> "<span class="hljs-attr">Full</span> <span class="hljs-attr">Name</span> <span class="hljs-attr">is</span> <span class="hljs-attr">required</span>"
    })}
    <span class="hljs-attr">id</span>=<span class="hljs-string">"fullname"</span>
    <span class="hljs-attr">aria-required</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Full name"</span>
    <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"</span>
/&gt;</span>
</code></pre>
<p>Why this is important for accessibility:</p>
<h4 id="heading-1-screen-readers">1. Screen readers:</h4>
<p>When a screen reader lands on the <code>&lt;input&gt;</code>, it automatically reads the associated label ("Full Name"). Even if the label is not visually right next to the input, the screen reader still knows which text describes the input, giving you some freedom when designing your forms.</p>
<h4 id="heading-2-click-behaviour">2. Click behaviour:</h4>
<p>When you click the <code>&lt;label&gt;</code>, it automatically focuses the <code>&lt;input&gt;</code> when using <code>htmlFor</code>.</p>
<p>Users don’t have to click exactly on the tiny input field – and this can certainly be useful when dealing with checkboxes or radio buttons, for example.</p>
<p>In short, big click targets = better usability and faster form filling.</p>
<p>This is also very helpful for mobile users where precision tapping is hard, especially on smaller screens.</p>
<h2 id="heading-give-additional-information-with-aria-describedby"><strong>Give Additional Information With</strong> <code>aria-describedBy</code></h2>
<p>Now that we’ve added clear labels to our form fields, we can take accessibility a step further by providing additional guidance for users when errors occur. By using <code>aria-describedby</code> and <code>aria-invalid</code>, we can link helpful error messages to the input fields and ensure screen readers communicate validation issues clearly. Let’s look at how to implement this:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"email"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-white"</span>&gt;</span>Email<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">...register</span>("<span class="hljs-attr">email</span>", {
          <span class="hljs-attr">required:</span> "<span class="hljs-attr">You</span> <span class="hljs-attr">must</span> <span class="hljs-attr">enter</span> <span class="hljs-attr">an</span> <span class="hljs-attr">email</span> <span class="hljs-attr">address</span>",
      <span class="hljs-attr">pattern:</span> {
        <span class="hljs-attr">value:</span> /^\<span class="hljs-attr">S</span>+@\<span class="hljs-attr">S</span>+$/<span class="hljs-attr">i</span>,
        <span class="hljs-attr">message:</span> "<span class="hljs-attr">Invalid</span> <span class="hljs-attr">email</span> <span class="hljs-attr">address</span>",
      },
    })}
    <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span>
    <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
    <span class="hljs-attr">aria-invalid</span>=<span class="hljs-string">{errors.email</span> ? "<span class="hljs-attr">true</span>" <span class="hljs-attr">:</span> "<span class="hljs-attr">false</span>"}
    <span class="hljs-attr">aria-describedby</span>=<span class="hljs-string">{errors.email</span> ? "<span class="hljs-attr">email-error</span>" <span class="hljs-attr">:</span> <span class="hljs-attr">undefined</span>}
    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter your email address"</span>
    <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full p-3 rounded bg-gray-700 text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-red-500"</span>
  /&gt;</span>
  {errors.email &amp;&amp; (
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"email-error"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500 text-sm mt-1"</span>&gt;</span>
      {errors.email.message}
    <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>
</code></pre>
<p>Notice the two new attributes we’ve added:</p>
<ul>
<li><p><code>aria-describedBy</code> – this attribute links our error message with our input. Screen readers will therefore read out the error message whilst reading out other information when the input is focused.</p>
</li>
<li><p><code>aria-invalid</code> – this attribute again aids with screen readers, informing the user that the input’s value is invalid and they need to correct it. This combined with the <code>describedBy</code> attribute gives visually impaired users all the information they need in order to correct their mistake.</p>
</li>
</ul>
<h2 id="heading-avoid-tooltips-for-critical-information">Avoid Tooltips for Critical Information</h2>
<p>When developing your form, try to avoid tooltips (those little elements that show when you hover over another element for a period of time like below).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745685939025/ce427cf1-ef44-4021-b8d3-0095e7a091c6.png" alt="Example of a tooltip showing text that appears when a user hovers over a term." class="image--center mx-auto" width="824" height="228" loading="lazy"></p>
<p>The problems with using tooltips are:</p>
<ol>
<li><p>They often require <strong>mouse hover</strong>, which doesn't work on touch devices (for example mobile phones, or tablets).</p>
</li>
<li><p>They aren’t announced reliably by screen readers if proper aria labels aren’t added.</p>
</li>
<li><p>They disappear too quickly</p>
</li>
</ol>
<p>Instead, we can use inline helper text or descriptions combined with <code>aria-describedby</code> like below:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"passwordHint"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xs text-gray-500"</span>&gt;</span>
  Must be at least 8 characters and include a number.
<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>We can then reference this within our input using the <code>aria-describedBy</code> attribute. But wait, we already have a <code>describedBy</code> pointing at the error message – well, that’s ok! We can link multiple elements, like the brief example below:</p>
<pre><code class="lang-xml">// now references both passwordHint and the password error (we seperate the ids with a space)
<span class="hljs-tag">&lt;<span class="hljs-name">input</span> 
  <span class="hljs-attr">id</span>=<span class="hljs-string">"password"</span>
  <span class="hljs-attr">aria-describedby</span>=<span class="hljs-string">"passwordHint passwordError"</span>
/&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"passwordHint"</span>&gt;</span>
  Must be at least 8 characters long.
<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">id</span>=<span class="hljs-string">"passwordError"</span>&gt;</span>
  Passwords do not match!
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<h2 id="heading-tell-me-something-important">Tell Me Something Important</h2>
<p><code>aria-live</code> is an aria attribute you can add to an element to tell screen readers:</p>
<blockquote>
<p>"Hey, if the content inside me changes, announce it automatically."</p>
</blockquote>
<p>It makes dynamic content updates audible without needing the user to re-focus anything.</p>
<p>A basic example could look something like below, where a message which is updated upon submission is updated, it could contain something like:</p>
<blockquote>
<p>“Loading” → “<em>Hurray, registration complete”</em></p>
<p>or</p>
<p>““Pending” → “Registration failed due to many errors”</p>
</blockquote>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">aria-live</span>=<span class="hljs-string">"polite"</span>&gt;</span>
  {formSubmissionResultMessage}
<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>When <code>formSubmissionResultMessage</code> changes, screen readers will automatically announce the updated message.</p>
<p>The timing of when it is read out depends on the value of the <code>aria-live</code> attribute – with <code>polite</code>, the announcement waits for a natural pause. With <code>assertive</code>, it interrupts immediately.</p>
<h3 id="heading-real-world-examples">Real-World Examples</h3>
<h4 id="heading-polite-update-good-for-passive-notifications">Polite update: good for passive notifications</h4>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">aria-live</span>=<span class="hljs-string">"polite"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-2 text-green-500"</span>&gt;</span>
  Form saved successfully.
<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>The screen reader waits for a good moment to say it.</p>
<h4 id="heading-assertive-update-good-for-urgent-errors">Assertive update: good for urgent errors</h4>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">aria-live</span>=<span class="hljs-string">"assertive"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-2 text-red-500"</span>&gt;</span>
  Passwords do not match!
<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
</code></pre>
<p>The screen reader <strong>immediately</strong> interrupts and announces it.</p>
<h4 id="heading-good-things-to-know">Good things to know:</h4>
<ul>
<li><p>The element needs to <strong>already exist</strong> in the DOM when the update happens. So it’s smart to always render the <code>&lt;p aria-live&gt;</code> – just update its content.</p>
</li>
<li><p>Don’t overuse <code>assertive</code>, or you’ll annoy users and make apps feel super noisy and overwhelming.</p>
</li>
</ul>
<h2 id="heading-focus-states-and-colouring">Focus States and Colouring</h2>
<p>You may have noticed on the input elements that I have added some custom colouring with TailwindCSS classes <code>focus:</code>. But what is this doing?</p>
<p>Well, this allows us to control the focus colour of the inputs. Without this, the browser will apply its own default styling which may not be as accessible to our users, especially those with colour-blindness.</p>
<p>For example, within our form, without the styling the input with focus looks like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745687500364/4bfa9c86-d908-4f2b-a674-485a1ed15bc3.png" alt="image of the form with a faint white and blue outline around the focussed input" class="image--center mx-auto" width="514" height="673" loading="lazy"></p>
<p>Here you can see it has applied a subtle white and blue outline – but its not that clear it’s being focused. You can argue it is different enough to other input elements, but for some users this may not be enough.</p>
<p>To combat this and improve usability, we can override this with our own custom colouring. When using TailwindCSS, we can apply the following class names:</p>
<pre><code class="lang-xml">focus:outline-none focus:ring-2 focus:ring-red-500
</code></pre>
<h3 id="heading-what-does-this-do">What Does This Do?</h3>
<p>This now applies a much thicker red line (encompassing brand colours) as well as making it clearer against the darker background</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Class name</strong></td><td><strong>Meaning (CSS equivalent)</strong></td></tr>
</thead>
<tbody>
<tr>
<td><code>focus:outline-none</code></td><td>Remove the outline when the element is focused</td></tr>
<tr>
<td><code>focus:ring-2</code></td><td>On focus, apply a <strong>2px wide ring</strong> (like a border/shadow)</td></tr>
<tr>
<td><code>focus:ring-red-500</code></td><td>Set the ring colour to Tailwind’s <code>red-500</code> colour</td></tr>
</tbody>
</table>
</div><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745687470893/c003c194-5f93-491d-a16e-4d983ea557ac.png" alt="image of form with thick red outline around the focussed input" class="image--center mx-auto" width="537" height="677" loading="lazy"></p>
<p>If you’re not using TailwindCSS, you can accomplish the same with plain CSS like so:</p>
<pre><code class="lang-css"><span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:focus</span> {
  <span class="hljs-attribute">outline</span>: none; <span class="hljs-comment">/* no default browser outline */</span>
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">2px</span> <span class="hljs-number">#ef4444</span>; <span class="hljs-comment">/* 2px red ring around input */</span>
}
</code></pre>
<h2 id="heading-make-buttons-descriptive">Make Buttons Descriptive</h2>
<p>A super simple way to level up your form’s user experience is to make sure your buttons use clear, descriptive text.</p>
<p>Let’s take a look at a few examples of buttons that don’t quite achieve this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745688682031/54a84908-e0fd-4781-ab83-6c34ab360cae.png" alt="image showing different poor input buttons" class="image--center mx-auto" width="545" height="336" loading="lazy"></p>
<p>The above buttons are examples of poor input buttons because:</p>
<ul>
<li><p>“Click Here” doesn’t give any context. Screen reader users, and even sighted users, have no idea what "click here" does without reading nearby text.</p>
</li>
<li><p>Icon Only: Sighted users <em>might</em> guess what the icon means, but screen readers see nothing unless you add <code>aria-label</code>. The point is, it is ambiguous and unclear as to what the button does. You may see websites that just use an icon, not surrounded by a button, which can be even more confusing.</p>
</li>
<li><p>“Submit”: If you have several "Submit" buttons (for example, one for payment, one for contact form), users don't know which "submit" is doing what.</p>
</li>
</ul>
<h3 id="heading-improvements">Improvements</h3>
<p>Instead, we can improve those buttons to be more accessible and user-friendly by doing the following:</p>
<ul>
<li><p><strong>Use descriptive button text</strong> – for example: "Pay Now", "Sign Up", or "Save Changes".</p>
</li>
<li><p><strong>Use both an icon and text</strong> – combining an icon with text can be the perfect blend for both accessibility and design.</p>
</li>
<li><p><strong>Use</strong> <code>aria-label</code> – if you really must use an icon-only button (like a basket or home icon in a navigation bar), make sure to add an <code>aria-label</code> attribute to clearly describe the button’s purpose, like so:</p>
</li>
</ul>
<pre><code class="lang-xml"><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">className</span>=<span class="hljs-string">"w-full py-3 px-6 rounded-lg bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 text-white text-lg font-semibold transition"</span>
&gt;</span> Pay Now <span class="hljs-tag">&lt;<span class="hljs-name">button</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">className</span>=<span class="hljs-string">"w-full py-3 px-6 rounded-lg bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 text-white text-lg font-semibold flex justify-center items-center gap-2 transition"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">HomeIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-6 w-6"</span> /&gt;</span>
        Home
<span class="hljs-tag">&lt;/<span class="hljs-name">button</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">aria-label</span>=<span class="hljs-string">"Go to homepage"</span>
    <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full py-3 px-6 rounded-lg bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 text-white text-lg font-semibold flex justify-center items-center transition"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">HomeIcon</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-6 w-6"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>That code would generate the following:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745787493605/e5259e87-f8ee-463f-bc57-662779eea698.png" alt="image showing more accessible buttons from above html" class="image--center mx-auto" width="513" height="326" loading="lazy"></p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>In this tutorial, we’ve covered various ways to make your forms more accessible and user-friendly. From simple things like making button text clearer and using more user-friendly colours, to more complex HTML attributes like <code>aria-describedBy</code> and <code>aria-live</code>, you should be covered.</p>
<p>I hope you found this tutorial helpful, and now you’re ready to take your development skills to the next level. Making these simple changes can have a big impact on your users’ experience, and they’ll definitely stick around longer and be less frustrated.</p>
<p>As always, if you’d like to share feedback on the article, discuss it further, or just hear about future articles or content, you can drop me a follow on X (Twitter) via my handle <a target="_blank" href="https://x.com/grantdotdev">@grantdotdev</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use TestContainers in .Net ]]>
                </title>
                <description>
                    <![CDATA[ At some point in your development lifecycle, you will need to test that your system can integrate with another system, whether it be another API, a database, or caching service, for example. This can be a laborious task of spinning up other servers h... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-testcontainers-in-net/</link>
                <guid isPermaLink="false">67e2cbfdaa97659cd53cf39f</guid>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testcontainers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Integration Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Tue, 25 Mar 2025 15:30:05 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742773343798/44c64acc-3862-4325-af21-6b7de417d300.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>At some point in your development lifecycle, you will need to test that your system can integrate with another system, whether it be another API, a database, or caching service, for example. This can be a laborious task of spinning up other servers hosting the 3rd party API replica, or permanently hosting a SQL database seeded with test data.</p>
<p>In this article, I’ll teach you how to use the TestContainers library to make running integration tests much easier and more manageable.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-testcontainers">What Is TestContainers?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-does-it-all-work">How Does It All Work?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-your-first-test">How to Set Up Your First Test</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-key-behaviors-of-iasynclifetime-in-a-test-class">Key Behaviors of IAsyncLifetime in a Test Class</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-improve-performance">How to Improve Performance</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-explanation-of-differences">Explanation of Differences</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-share-your-container-across-multiple-test-classes">How to Share Your Container Across Multiple Test Classes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary-of-approaches">Summary of Approaches:</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-multiple-containers">How to Create Multiple Containers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-make-your-setup-easier-with-custom-images">How to Make Your Setup Easier With Custom Images</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Understanding of Docker</p>
</li>
<li><p>Understanding of xUnit and testing</p>
</li>
<li><p>Installation of the following packages:</p>
<ul>
<li><p><code>TestContainers</code></p>
</li>
<li><p><code>TestContainers.MsSql</code></p>
</li>
<li><p>xUnit</p>
</li>
<li><p>&gt;= .Net 8</p>
</li>
<li><p><code>FluentAssertions</code></p>
</li>
<li><p><code>Microsoft.Data.SqlClient</code></p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-what-is-testcontainers">What Is TestContainers?</h2>
<p><a target="_blank" href="https://testcontainers.com">TestContainers</a> is an open source library that provides you with easily disposable container instances for things like database hosting, message brokers, browsers and more – basically anything that can run in a Docker container.</p>
<p>It removes the necessity to maintain hosted environments for testing in the cloud or on local machines. As long as the user’s machine and CI/CD host supports Docker, the testContainer tests can easily be run.</p>
<h2 id="heading-how-does-it-all-work">How Does It All Work?</h2>
<p>You define the image you’re wanting to utilise, and specify a configuration.</p>
<p>The TestContainer library spins up a Docker Container with the configured image.</p>
<h3 id="heading-provides-connection-details"><strong>Provides Connection Details</strong></h3>
<p>After starting the container, TestContainers exposes connection strings (for example, a database connection URL), so your tests can use the real service, rather than having to configure this yourself.</p>
<h3 id="heading-cleans-up-automatically"><strong>Cleans Up Automatically</strong></h3>
<p>When the test finishes, TestContainers removes the container automatically, ensuring no leftover resources. This is one of the best things about using TestContainers: all the creation, tear down, and container setup is handled within the library itself, making it perfect for use within delivery pipelines.</p>
<h2 id="heading-how-to-set-up-your-first-test">How to Set Up Your First Test</h2>
<p>For the purpose of this tutorial, we’re going to keep things simple, and only use a <code>MS Sql Server</code> image.</p>
<p>The first thing we’re going to do is configure our Microsoft SQL Server Docker container via the TestContainer fluid API.</p>
<p>Create your test class like below:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">IntegrationTests</span>: <span class="hljs-title">IAsyncLifetime</span> 
{
    <span class="hljs-keyword">private</span> MsSqlContainer _container;
    <span class="hljs-keyword">private</span> FakeLogger _<span class="hljs-function">logger

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">InitializeAsync</span>(<span class="hljs-params"></span>)</span>
    {
           _container = <span class="hljs-keyword">new</span> MsSqlBuilder()
                .WithImage(<span class="hljs-string">"mcr.microsoft.com/mssql/server:2022-latest"</span>)
                .WithPassword(<span class="hljs-string">"P@ssw0rd123"</span>)
                .WithPortBinding(<span class="hljs-number">1443</span>)
                .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(<span class="hljs-number">1433</span>))
                .Build();

            _logger = <span class="hljs-keyword">new</span> FakeLogger();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">DisposeAsync</span>(<span class="hljs-params"></span>)</span> =&gt; <span class="hljs-keyword">await</span> _container.DisposeAsync();
}
</code></pre>
<p>Here we’re using xUnit’s <code>IAsyncLifetime</code> interface. It’s an interface in xUnit that provides a way to handle async setup and teardown for test classes. It's useful when you need to initialise and clean up resources asynchronously. We’re using the <code>InitializeAsync()</code> to setup and define our Microsoft SQL Database container as well as starting the container, then using the <code>DisposeAsync()</code> method to stop and dispose of our container.</p>
<h3 id="heading-explanation-of-builder-methods">Explanation of Builder Methods</h3>
<ul>
<li><p><code>WithImage()</code>: this allows us to specify the image we want Docker to pull down and run. We’ve opted for the latest version of SQL Server 2022.</p>
</li>
<li><p><code>WithPassword()</code>: This allows us to specify the password for the database (when creating most databases, a password is normally required).</p>
</li>
<li><p><code>WithPortBinding()</code>: This allows us to specify both the hosting port number on your machine, as well as the container port number</p>
</li>
<li><p><code>WithWaitStrategy()</code>: Here we can specify a wait strategy, which informs our container to wait for a condition before the container is ready to use. This is important because some services (like databases or APIs) take time to fully start up.</p>
</li>
<li><p><code>Build()</code>" This is the command that builds the test container based on the configuration. This <strong>does not</strong> run or start the container – you can do this using the <code>container.StartAsync()</code> method as mentioned previously.</p>
</li>
</ul>
<h4 id="heading-why-is-withwaitstrategy-needed"><strong>Why Is</strong> <code>WithWaitStrategy()</code> Needed?</h4>
<p>By default, TestContainers assumes the container is ready as soon as it starts running. But some services might:</p>
<ul>
<li><p>Take time to initialize.</p>
</li>
<li><p>Require a specific log message before they are ready.</p>
</li>
<li><p>Need a port to be accessible before you can connect.</p>
</li>
</ul>
<p>Using <code>WithWaitStrategy()</code>, you can customise how TestContainers waits before considering the container "ready."</p>
<h3 id="heading-adding-the-test">Adding the Test</h3>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">IntegrationTests</span>: <span class="hljs-title">IAsyncLifetime</span> 
{
    <span class="hljs-keyword">private</span> MsSqlContainer _container;
    <span class="hljs-keyword">private</span> FakeLoger _logger;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">InitializeAsync</span>(<span class="hljs-params"></span>)</span>
    {
           _container = <span class="hljs-keyword">new</span> MsSqlBuilder()
                .WithImage(<span class="hljs-string">"mcr.microsoft.com/mssql/server:2022-latest"</span>)
                .WithPassword(<span class="hljs-string">"P@ssw0rd123"</span>)
                .WithPortBinding(<span class="hljs-number">1443</span>)
                .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(<span class="hljs-number">1433</span>))
                .Build();

            <span class="hljs-keyword">await</span> _container.StartAsync();
            _logger = <span class="hljs-keyword">new</span> FakeLogger();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">DisposeAsync</span>(<span class="hljs-params"></span>)</span> =&gt; <span class="hljs-keyword">await</span> _container.DisposeAsync();

    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">Test_Database_Connection</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">var</span> connectionString = _container.GetConnectionString();
        <span class="hljs-keyword">using</span> <span class="hljs-keyword">var</span> conn = <span class="hljs-keyword">new</span> SqlConnection(connectionString);
        <span class="hljs-keyword">await</span> conn.OpenAsync();

        Assert.True(conn.State == System.Data.ConnectionState.Open);
    }
}
</code></pre>
<p>The above test, although it’s simple, illustrates how easy it is to spin up a container and create a simple test. The above test will work, but it can lead to low performing tests and high usage of machine resource when not used correctly. Let me explain:</p>
<p>Using <code>IAsyncLifetime</code> is necessary, as we’re calling async setup methods (<code>StartAsync</code>), for example. But the <code>InitializeAsync() / DisposeAsync()</code> methods when situated in a test class are run before and after every test (<code>Fact</code> in xUnit).</p>
<p>This means that every time a test begins, it is:</p>
<ul>
<li><p>creating a brand new Docker container,</p>
</li>
<li><p>pulling the MS Sql image,</p>
</li>
<li><p>creating the DB,</p>
</li>
<li><p>running the tests, and</p>
</li>
<li><p>tearing down the container.</p>
</li>
</ul>
<p>You can test this by copying and pasting the above <code>Test_Database_Connection()</code> test multiple times, adding a number to each duplicate test (to keep the compiler happy), and opening Docker Desktop. Running all the tests, you will see a new container (with a different name) being created for each test run.</p>
<p>Now, this can be acceptable if you have a limited number of tests in your test class. But it can have negative outcomes on test classes with a larger number of tests, meaning test maintenance and planning is key. It’s useful, though, when you want to make sure that the database is in a completely clean state before each test, ensuring no data contamination from other tests running.</p>
<h2 id="heading-key-behaviors-of-iasynclifetime-in-a-test-class"><strong>Key Behaviors of</strong> <code>IAsyncLifetime</code> in a Test Class</h2>
<p>When your test class implements <code>IAsyncLifetime</code>, xUnit's default behaviour is:</p>
<p>1. Creates a new instance of the test class for each test method.<br>2. Calls <code>InitializeAsync()</code> before each test.<br>3. Calls <code>DisposeAsync()</code> after each test.</p>
<h3 id="heading-what-does-this-mean-for-testcontainers"><strong>What Does This Mean for TestContainers?</strong></h3>
<ul>
<li><p>In our case, since <code>InitializeAsync()</code> sets up a new container, a new container is created for each test.</p>
</li>
<li><p><code>DisposeAsync()</code> stops the container after each test finishes.</p>
</li>
<li><p>Ensures a completely fresh database state for every test, avoiding data contamination.</p>
</li>
<li><p>Is slow and resource-intensive, especially if you have many test methods.</p>
</li>
</ul>
<p>A more visual look on a test class could look like this:</p>
<p>🟢 InitializeAsync() -&gt; New Container Created (For Test_1)</p>
<p>🧪 Running Test_1</p>
<p><strong>🛑</strong> DisposeAsync() -&gt; Container Stopped (After Test_1)</p>
<p>🟢 InitializeAsync() -&gt; New Container Created (For Test_2)</p>
<p>🧪 Running Test_2</p>
<p><strong>🛑</strong> DisposeAsync() -&gt; Container Stopped (After Test_2)</p>
<h3 id="heading-when-is-this-useful"><strong>When Is This Useful?</strong></h3>
<ul>
<li><p>You need a completely fresh database state or container for each test.</p>
</li>
<li><p>Avoids test data contamination.</p>
</li>
<li><p>Each test starts from a clean slate.</p>
</li>
</ul>
<h3 id="heading-when-is-this-a-problem"><strong>When Is This a Problem?</strong></h3>
<ul>
<li><p>It results in slow execution – a new container is started for every test.</p>
</li>
<li><p>It’s resource-heavy – multiple containers run sequentially.</p>
</li>
<li><p>And it’s not scalable – hundreds of tests will take a long time to complete.</p>
</li>
</ul>
<h2 id="heading-how-to-improve-performance">How to Improve Performance</h2>
<p>Ok, so we’ve seen how to create containers once per test, and explored scenarios where this would be useful, but what if performance and cost are a concern?</p>
<p>Here we can combine <code>IClassFixture</code> and <code>IAsyncLiftetime</code> to achieve a <em>Once per test class</em> approach, where we create one container and one database, and its lifecycle is the full length of the test class (that is, all tests run against the same DB).</p>
<h3 id="heading-how-to-write-this">How to Write This</h3>
<p>We can utilise a TestFixture class which inherits the IAsyncLifetime interface, exposing the <code>InitializeAsync()</code> and <code>DisposeAsync()</code> methods as before.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> DotNet.Testcontainers.Builders;
<span class="hljs-keyword">using</span> Microsoft.Extensions.Logging.Testing;
<span class="hljs-keyword">using</span> Testcontainers.MsSql;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">IntegrationTests</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">TestClassFixture</span> : <span class="hljs-title">IAsyncLifetime</span>
{
    <span class="hljs-keyword">public</span> MsSqlContainer Container { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">private</span> FakeLogger _logger;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">InitializeAsync</span>(<span class="hljs-params"></span>)</span>
    {
        Container = <span class="hljs-keyword">new</span> MsSqlBuilder()
            .WithImage(<span class="hljs-string">"mcr.microsoft.com/mssql/server:2022-latest"</span>)
            .WithPassword(<span class="hljs-string">"P@ssw0rd123"</span>)
            .WithPortBinding(<span class="hljs-number">1443</span>)
            .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(<span class="hljs-number">1433</span>))
            .Build();

        _logger = <span class="hljs-keyword">new</span> FakeLogger();
        <span class="hljs-keyword">await</span> Container.StartAsync();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">DisposeAsync</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">await</span> Container.DisposeAsync();
    }
}
</code></pre>
<p>Using xUnit’s <code>IClassFixture</code> interface, we can pass our <code>TestClassFixture</code> and have our test class inherit from this. A test fixture is only run once per test class, making it perfect for our scenario.</p>
<pre><code class="lang-csharp">
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">IntegrationFixtureTests</span> : <span class="hljs-title">IClassFixture</span>&lt;<span class="hljs-title">TestClassFixture</span>&gt;
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">string</span> _connectionString;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">IntegrationFixtureTests</span>(<span class="hljs-params">TestClassFixture testClassFixture</span>)</span>
    {
        _connectionString = testClassFixture.Container.GetConnectionString();

        <span class="hljs-comment">// other test class specific setup goes here</span>
    }

    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">Test_Database_Connection</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">await</span> <span class="hljs-keyword">using</span> <span class="hljs-keyword">var</span> conn = <span class="hljs-keyword">new</span> SqlConnection(_connectionString);
        <span class="hljs-keyword">await</span> conn.OpenAsync();

        Assert.True(conn.State == System.Data.ConnectionState.Open);
    }
}
</code></pre>
<p>We now have a much cleaner test class, and all our container logic is handled by the <code>IClassFixture</code> instead. Should you need to add test class specific code, for example seeding the database prior to running, or the mocking of any resources, you can place this code within the constructor.</p>
<h2 id="heading-explanation-of-differences">Explanation of Differences</h2>
<p>We set our <code>Container</code> property as public, rather than private so that our test class can access the container. The test fixture is injected by xUnit's own internal dependency injection mechanics when you use <code>IClassFixture&lt;T&gt;</code>.</p>
<p>xUnit automatically creates an instance of the fixture class and passes it into the test class constructor.</p>
<p>The container is started within the <code>InitializeAsync()</code> method on the <strong>TestFixture</strong> now, rather than the test class, meaning it only gets started once and is readily available for all the tests. This improves performance and test speeds (no more waiting for each container to spin up before each test).</p>
<p>The test flow would look something more like this now:</p>
<p>🟢 InitializeAsync() → Container Created → Container Started</p>
<p>🧪 Running Test_1</p>
<p>🧪 Running Test_2</p>
<p><strong>🛑</strong> DisposeAsync() -&gt; Container Stopped → Container Disposed of</p>
<h3 id="heading-advantages-and-disadvantages">Advantages and Disadvantages</h3>
<h4 id="heading-faster-execution">✅ <strong>Faster Execution</strong></h4>
<p>Significantly reduces setup/teardown overhead, especially when using slow-starting services like databases.</p>
<h4 id="heading-lower-resource-usage">✅ <strong>Lower Resource Usage</strong></h4>
<p>Running a container once per test class consumes far fewer system resources compared to one container per test. This is especially beneficial when running integration tests in CI/CD pipelines where resource usage needs to be optimised to keep costs low.</p>
<h4 id="heading-more-realistic-testing">✅ <strong>More Realistic Testing</strong></h4>
<p>In real-world scenarios, applications don’t restart their databases between API calls, so why should your integration tests?</p>
<h4 id="heading-data-contamination">❌ <strong>Data Contamination</strong></h4>
<p>Effective test data management is essential for maintaining reliable tests. If test data is not properly isolated, it can lead to unintended interference between tests.</p>
<p>For example, a test that creates a new record might introduce unexpected data, causing a retrieval test to fail if it runs afterward. This type of data contamination is a common issue when all tests in a test class share the same database setup. But,with careful test design—such as proper data isolation, cleanup strategies, or using transactional rollbacks—these issues can be mitigated or entirely avoided.</p>
<h4 id="heading-more-care-needs-to-be-taken-around-indempotency">❌ <strong>More Care Needs To Be Taken Around Indempotency</strong></h4>
<p>“Indempotency” refers to the ability to run any test on its own in any order. If the test class is accessing data from the same areas, the assertions may fail when ran in certain orders than others. For example:</p>
<ul>
<li><p>Test_1 inserts a record.</p>
</li>
<li><p>Test_2 assumes the table is empty and asserts <code>QueryByName()</code> should return 1 record</p>
</li>
<li><p>Test_2 fails because Test_1 has already inserted its own record</p>
</li>
</ul>
<h2 id="heading-how-to-share-your-container-across-multiple-test-classes">How to Share Your Container Across Multiple Test Classes</h2>
<p>So we’ve covered a container per test and a container per test class. But what about sharing a container for multiple test classes? Well, it’s as simple as using the <code>ICollectionFixture</code> interface instead of <code>IClassFixture</code>, and it can be used like so:</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">CollectionDefinition(<span class="hljs-meta-string">"Database collection"</span>)</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">DatabaseCollection</span> : <span class="hljs-title">ICollectionFixture</span>&lt;<span class="hljs-title">TestClassFixture</span>&gt;
{
    <span class="hljs-comment">// This class has no code, </span>
    <span class="hljs-comment">// it’s just used to apply the [Collection] attribute to test classes.</span>
}
</code></pre>
<p>The <code>ICollectionFixture&lt;T&gt;</code> mechanism in xUnit automatically ties the fixture instance to all test classes marked with the <code>[Collection("Collection Name")]</code> attribute, for example:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> IntegrationTests;
<span class="hljs-keyword">using</span> Microsoft.Data.SqlClient;

[<span class="hljs-meta">Collection(<span class="hljs-meta-string">"Database collection"</span>)</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">IntegrationFixtureTests</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">string</span> _connectionString;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">IntegrationFixtureTests</span>(<span class="hljs-params">TestClassFixture testClassFixture</span>)</span>
    {
        _connectionString = testClassFixture.Container.GetConnectionString();
    }

    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">Test_Database_Connection</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">await</span> <span class="hljs-keyword">using</span> <span class="hljs-keyword">var</span> conn = <span class="hljs-keyword">new</span> SqlConnection(_connectionString);
        <span class="hljs-keyword">await</span> conn.OpenAsync();

        Assert.True(conn.State == System.Data.ConnectionState.Open);
    }
}

[<span class="hljs-meta">Collection(<span class="hljs-meta-string">"Database collection"</span>)</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">AnotherIntegrationTest</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">string</span> _connectionString;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AnotherIntegrationTest</span>(<span class="hljs-params">TestClassFixture testClassFixture</span>)</span>
    {
        _connectionString = testClassFixture.Container.GetConnectionString();
    }

    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">Another_Database_Test</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">await</span> <span class="hljs-keyword">using</span> <span class="hljs-keyword">var</span> conn = <span class="hljs-keyword">new</span> SqlConnection(_connectionString);
        <span class="hljs-keyword">await</span> conn.OpenAsync();

        Assert.True(conn.State == System.Data.ConnectionState.Open);
    }
}
</code></pre>
<p>Now you can group your integration tests, whether it be all read tests or all write tests – making your tests much more maintainable.</p>
<h2 id="heading-summary-of-approaches">Summary of Approaches:</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Approach</strong></td><td><strong>Container Creation</strong></td><td><strong>Best For</strong></td></tr>
</thead>
<tbody>
<tr>
<td><code>IAsyncLifetime</code> inside the test class</td><td><strong>One per test</strong></td><td>When a fresh DB state per test is needed, avoiding test contamination</td></tr>
<tr>
<td><code>IClassFixture&lt;T&gt;</code> with <code>IAsyncLifetime</code></td><td><strong>One per test class</strong></td><td>Faster execution, sharing DB instance across tests in a class</td></tr>
<tr>
<td><code>ICollectionFixture&lt;T&gt;</code> with <code>IAsyncLifetime</code></td><td><strong>One per multiple test classes</strong></td><td>Sharing a DB instance across different test classes</td></tr>
</tbody>
</table>
</div><h2 id="heading-how-to-create-multiple-containers">How to Create Multiple Containers</h2>
<p>Yes, you can create multiple containers which can host different images, making it perfect for when you have multiple systems you need to integrate with – for example Microsoft SQL Server and a Redis instance.</p>
<p>You can do this by calling the constructor of the relevant TestContainer package like below:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">TestContainersFixture</span> : <span class="hljs-title">IAsyncLifetime</span>
{
    <span class="hljs-keyword">public</span> MsSqlContainer SqlContainer { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">private</span> <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> RedisContainer RedisContainer { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">private</span> <span class="hljs-keyword">set</span>; }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">InitializeAsync</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// SQL Server Container</span>
        SqlContainer = <span class="hljs-keyword">new</span> MsSqlBuilder()
            .WithImage(<span class="hljs-string">"mcr.microsoft.com/mssql/server:2022-latest"</span>)
            .WithPassword(<span class="hljs-string">"P@ssw0rd123"</span>)
            .WithPortBinding(<span class="hljs-number">1433</span>)
            .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(<span class="hljs-number">1433</span>))
            .Build();

        <span class="hljs-comment">// Redis Container</span>
        RedisContainer = <span class="hljs-keyword">new</span> RedisContainerBuilder()
            .WithImage(<span class="hljs-string">"redis:latest"</span>)
            .WithPortBinding(<span class="hljs-number">6379</span>)
            .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(<span class="hljs-number">6379</span>))
            .Build();

        <span class="hljs-keyword">await</span> Task.WhenAll(SqlContainer.StartAsync(), RedisContainer.StartAsync());
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">DisposeAsync</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">await</span> Task.WhenAll(SqlContainer.DisposeAsync(), RedisContainer.DisposeAsync());
    }
}
</code></pre>
<p>And just like that, we have a SQL Server and a Redis instance ready to integrate test against.</p>
<h2 id="heading-how-to-make-your-setup-easier-with-custom-images">How to Make Your Setup Easier With Custom Images</h2>
<p>To make testing easier, and leverage the power of Docker and TestContainers, here’s a great tip. TestContainers fully supports using custom images, including pre-configured ones with seeded databases. Instead of defining everything in the test setup, you can build and use a custom Docker image that already contains the required schema and test data.</p>
<p>When creating your own custom package to use, you can:</p>
<ol>
<li>Upload your custom image to DockerHub and reference from there:</li>
</ol>
<pre><code class="lang-csharp"> SqlContainer = <span class="hljs-keyword">new</span> MsSqlBuilder()
            .WithImage(<span class="hljs-string">"your-dockerhub-username/custom-sql-image"</span>) 
            .WithPassword(<span class="hljs-string">"P@ssw0rd123"</span>)
            .WithPortBinding(<span class="hljs-number">1433</span>)
            .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(<span class="hljs-number">1433</span>))
            .Build();
</code></pre>
<ol start="2">
<li>Build your Docker image locally - f you're using a local image in TestContainers, you can simply reference the image name (e.g., <code>my-custom-sql-image</code>) in your code. TestContainers will first check your local Docker Desktop for the image before attempting to pull it from a registry like Docker Hub.</li>
</ol>
<pre><code class="lang-csharp">SqlContainer = <span class="hljs-keyword">new</span> MsSqlBuilder()
    .WithImage(<span class="hljs-string">"custom-sql-image"</span>) <span class="hljs-comment">// Reference your local image</span>
    .WithPassword(<span class="hljs-string">"P@ssw0rd123"</span>)
    .WithPortBinding(<span class="hljs-number">1433</span>)
    .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(<span class="hljs-number">1433</span>))
    .Build();
</code></pre>
<p>Having a pre-built image can speed up your tests especially in CI/CD pipelines, not to mention make them more readable by removing the seeding code.</p>
<p>To access your custom image in a CI/CD pipeline, you can upload it to DockerHub or GitHub Container Registry (GHCR) and access it from your tests. Build your DockerFile and push it to either system before accessing it in your tests.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Using TestContainers in .NET is a game-changer for integration testing. It’s a lightweight and automated way to manage external dependencies like databases, caching systems, and more. By using test containers in a test class, TestFixture, or ICollectionFixture, you can create cleaner, more reliable tests with isolated environments.</p>
<p>TestContainers can also save you money by eliminating the need for dedicated testing environments with long-lived dependencies. You can create and destroy them on the fly, or even integrate them into your CI/CD pipelines, especially in GitHub where Docker can be easily used.</p>
<p>As always I hope you’ve found this article helpful, and if you have any questions don’t hesitate to reach out on X / Twitter - <a target="_blank" href="https://x.com/grantdotdev">@grantdotdev</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How Python Magic Methods Work: A Practical Guide ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever wondered how Python makes objects work with operators like + or -? Or how it knows how to display objects when you print them? The answer lies in Python's magic methods, also known as dunder (double under) methods. Magic methods are spe... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/python-magic-methods-practical-guide/</link>
                <guid isPermaLink="false">67dc33ff64b6723cee52486b</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ python magic method ]]>
                    </category>
                
                    <category>
                        <![CDATA[ dunder method ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python advanced ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Vivek Sahu ]]>
                </dc:creator>
                <pubDate>Thu, 20 Mar 2025 15:27:59 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742482738702/0b357de2-855d-47c2-960f-453e0bfd9a3d.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever wondered how Python makes objects work with operators like <code>+</code> or <code>-</code>? Or how it knows how to display objects when you print them? The answer lies in Python's magic methods, also known as dunder (d<s>ouble</s> under) methods.</p>
<p>Magic methods are special methods that let you define how your objects behave in response to various operations and built-in functions. They're what makes Python's object-oriented programming so powerful and intuitive.</p>
<p>In this guide, you'll learn how to use magic methods to create more elegant and powerful code. You'll see practical examples that show how these methods work in real-world scenarios.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Basic understanding of Python syntax and object-oriented programming concepts.</p>
</li>
<li><p>Familiarity with classes, objects, and inheritance.</p>
</li>
<li><p>Knowledge of built-in Python data types (lists, dictionaries, and so on).</p>
</li>
<li><p>A working Python 3 installation is recommended to actively engage with the examples here.</p>
</li>
</ul>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ol>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#what-are-magic-methods">What are Magic Methods?</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#object-representation">Object Representation</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#str-vs-repr"><strong>str</strong> vs <strong>repr</strong></a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#practical-example-custom-error-class">Practical Example: Custom Error Class</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#operator-overloading">Operator Overloading</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#arithmetic-operators">Arithmetic Operators</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#comparison-operators">Comparison Operators</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#practical-example-money-class">Practical Example: Money Class</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#container-methods">Container Methods</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#sequence-protocol">Sequence Protocol</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#mapping-protocol">Mapping Protocol</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#practical-example-custom-cache">Practical Example: Custom Cache</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#attribute-access">Attribute Access</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#getattr-and-getattribute"><strong>getattr</strong> and <strong>getattribute</strong></a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#setattr-and-delattr"><strong>setattr</strong> and <strong>delattr</strong></a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#practical-example-auto-logging-properties">Practical Example: Auto-Logging Properties</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#context-managers">Context Managers</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#enter-and-exit"><strong>enter</strong> and <strong>exit</strong></a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#practical-example-database-connection-manager">Practical Example: Database Connection Manager</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#callable-objects">Callable Objects</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#call"><strong>call</strong></a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#practical-example-memoization-decorator">Practical Example: Memoization Decorator</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#advanced-magic-methods">Advanced Magic Methods</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#new-for-object-creation"><strong>new</strong> for Object Creation</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#slots-for-memory-optimization"><strong>slots</strong> for Memory Optimization</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#missing-for-default-dictionary-values"><strong>missing</strong> for Default Dictionary Values</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#performance-considerations">Performance Considerations</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#best-practices">Best Practices</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#references">References</a></p>
</li>
</ol>
<h2 id="heading-what-are-magic-methods"><strong>What are Magic Methods?</strong></h2>
<p>Magic methods in Python are special methods that start and end with double underscores (<code>__</code>). When you use certain operations or functions on your objects, Python automatically calls these methods.</p>
<p>For example, when you use the <code>+</code> operator on two objects, Python looks for the <code>__add__</code> method in the left operand. If it finds it, it calls that method with the right operand as an argument.</p>
<p>Here's a simple example that shows how this works:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Point</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, x, y</span>):</span>
        self.x = x
        self.y = y

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__add__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">return</span> Point(self.x + other.x, self.y + other.y)

p1 = Point(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>)
p2 = Point(<span class="hljs-number">3</span>, <span class="hljs-number">4</span>)
p3 = p1 + p2  <span class="hljs-comment"># This calls p1.__add__(p2)</span>
print(p3.x, p3.y)  <span class="hljs-comment"># Output: 4 6</span>
</code></pre>
<p>Let's break down what's happening here:</p>
<ol>
<li><p>We create a <code>Point</code> class that represents a point in 2D space</p>
</li>
<li><p>The <code>__init__</code> method initializes the x and y coordinates</p>
</li>
<li><p>The <code>__add__</code> method defines what happens when we add two points</p>
</li>
<li><p>When we write <code>p1 + p2</code>, Python automatically calls <code>p1.__add__(p2)</code></p>
</li>
<li><p>The result is a new <code>Point</code> with coordinates (4, 6)</p>
</li>
</ol>
<p>This is just the beginning. Python has many magic methods that let you customize how your objects behave in different situations. Let's explore some of the most useful ones.</p>
<h2 id="heading-object-representation"><strong>Object Representation</strong></h2>
<p>When you work with objects in Python, you often need to convert them to strings. This happens when you print an object or try to display it in the interactive console. Python provides two magic methods for this purpose: <code>__str__</code> and <code>__repr__</code>.</p>
<h3 id="heading-str-vs-repr"><strong>str vs repr</strong></h3>
<p>The <code>__str__</code> and <code>__repr__</code> methods serve different purposes:</p>
<ul>
<li><p><code>__str__</code>: Called by the <code>str()</code> function and by the <code>print()</code> function. It should return a string that is readable for end-users.</p>
</li>
<li><p><code>__repr__</code>: Called by the <code>repr()</code> function and used in the interactive console. It should return a string that, ideally, could be used to recreate the object.</p>
</li>
</ul>
<p>Here's an example that shows the difference:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Temperature</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, celsius</span>):</span>
        self.celsius = celsius

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__str__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"<span class="hljs-subst">{self.celsius}</span>°C"</span>

    <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"Temperature(<span class="hljs-subst">{self.celsius}</span>)"</span>

temp = Temperature(<span class="hljs-number">25</span>)
print(str(temp))      <span class="hljs-comment"># Output: 25°C</span>
print(repr(temp))     <span class="hljs-comment"># Output: Temperature(25)</span>
</code></pre>
<p>In this example:</p>
<ul>
<li><p><code>__str__</code> returns a user-friendly string showing the temperature with a degree symbol</p>
</li>
<li><p><code>__repr__</code> returns a string that shows how to create the object, which is useful for debugging</p>
</li>
</ul>
<p>The difference becomes clear when you use these objects in different contexts:</p>
<ul>
<li><p>When you print the temperature, you see the user-friendly version: <code>25°C</code></p>
</li>
<li><p>When you inspect the object in the Python console, you see the detailed version: <code>Temperature(25)</code></p>
</li>
</ul>
<h3 id="heading-practical-example-custom-error-class"><strong>Practical Example: Custom Error Class</strong></h3>
<p>Let's create a custom error class that provides better debugging information. This example shows how you can use <code>__str__</code> and <code>__repr__</code> to make your error messages more helpful:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ValidationError</span>(<span class="hljs-params">Exception</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, field, message, value=None</span>):</span>
        self.field = field
        self.message = message
        self.value = value
        super().__init__(self.message)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__str__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">if</span> self.value <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-string">f"Error in field '<span class="hljs-subst">{self.field}</span>': <span class="hljs-subst">{self.message}</span> (got: <span class="hljs-subst">{repr(self.value)}</span>)"</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Error in field '<span class="hljs-subst">{self.field}</span>': <span class="hljs-subst">{self.message}</span>"</span>

    <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">if</span> self.value <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-string">f"ValidationError(field='<span class="hljs-subst">{self.field}</span>', message='<span class="hljs-subst">{self.message}</span>', value=<span class="hljs-subst">{repr(self.value)}</span>)"</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"ValidationError(field='<span class="hljs-subst">{self.field}</span>', message='<span class="hljs-subst">{self.message}</span>')"</span>

<span class="hljs-comment"># Usage</span>
<span class="hljs-keyword">try</span>:
    age = <span class="hljs-number">-5</span>
    <span class="hljs-keyword">if</span> age &lt; <span class="hljs-number">0</span>:
        <span class="hljs-keyword">raise</span> ValidationError(<span class="hljs-string">"age"</span>, <span class="hljs-string">"Age must be positive"</span>, age)
<span class="hljs-keyword">except</span> ValidationError <span class="hljs-keyword">as</span> e:
    print(e)  <span class="hljs-comment"># Output: Error in field 'age': Age must be positive (got: -5)</span>
</code></pre>
<p>This custom error class provides several benefits:</p>
<ol>
<li><p>It includes the field name where the error occurred</p>
</li>
<li><p>It shows the actual value that caused the error</p>
</li>
<li><p>It provides both user-friendly and detailed error messages</p>
</li>
<li><p>It makes debugging easier by including all relevant information</p>
</li>
</ol>
<h2 id="heading-operator-overloading"><strong>Operator Overloading</strong></h2>
<p>Operator overloading is one of the most powerful features of Python's magic methods. It lets you define how your objects behave when used with operators like <code>+</code>, <code>-</code>, <code>*</code>, and <code>==</code>. This makes your code more intuitive and readable.</p>
<h3 id="heading-arithmetic-operators"><strong>Arithmetic Operators</strong></h3>
<p>Python provides magic methods for all basic arithmetic operations. Here's a table showing which method corresponds to which operator:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Operator</td><td>Magic Method</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td><code>+</code></td><td><code>__add__</code></td><td>Addition</td></tr>
<tr>
<td><code>-</code></td><td><code>__sub__</code></td><td>Subtraction</td></tr>
<tr>
<td><code>*</code></td><td><code>__mul__</code></td><td>Multiplication</td></tr>
<tr>
<td><code>/</code></td><td><code>__truediv__</code></td><td>Division</td></tr>
<tr>
<td><code>//</code></td><td><code>__floordiv__</code></td><td>Floor division</td></tr>
<tr>
<td><code>%</code></td><td><code>__mod__</code></td><td>Modulo</td></tr>
<tr>
<td><code>**</code></td><td><code>__pow__</code></td><td>Exponentiation</td></tr>
</tbody>
</table>
</div><h3 id="heading-comparison-operators"><strong>Comparison Operators</strong></h3>
<p>Similarly, you can define how your objects are compared using these magic methods:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Operator</td><td>Magic Method</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td><code>==</code></td><td><code>__eq__</code></td><td>Equal to</td></tr>
<tr>
<td><code>!=</code></td><td><code>__ne__</code></td><td>Not equal to</td></tr>
<tr>
<td><code>&lt;</code></td><td><code>__lt__</code></td><td>Less than</td></tr>
<tr>
<td><code>&gt;</code></td><td><code>__gt__</code></td><td>Greater than</td></tr>
<tr>
<td><code>&lt;=</code></td><td><code>__le__</code></td><td>Less than or equal to</td></tr>
<tr>
<td><code>&gt;=</code></td><td><code>__ge__</code></td><td>Greater than or equal to</td></tr>
</tbody>
</table>
</div><h3 id="heading-practical-example-money-class"><strong>Practical Example: Money Class</strong></h3>
<p>Let's create a <code>Money</code> class that handles currency operations correctly. This example shows how to implement multiple operators and handle edge cases:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> functools <span class="hljs-keyword">import</span> total_ordering
<span class="hljs-keyword">from</span> decimal <span class="hljs-keyword">import</span> Decimal

<span class="hljs-meta">@total_ordering  # Implements all comparison methods based on __eq__ and __lt__</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Money</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, amount, currency=<span class="hljs-string">"USD"</span></span>):</span>
        self.amount = Decimal(str(amount))
        self.currency = currency

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__add__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(other, Money):
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>
        <span class="hljs-keyword">if</span> self.currency != other.currency:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Cannot add different currencies: <span class="hljs-subst">{self.currency}</span> and <span class="hljs-subst">{other.currency}</span>"</span>)
        <span class="hljs-keyword">return</span> Money(self.amount + other.amount, self.currency)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__sub__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(other, Money):
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>
        <span class="hljs-keyword">if</span> self.currency != other.currency:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Cannot subtract different currencies: <span class="hljs-subst">{self.currency}</span> and <span class="hljs-subst">{other.currency}</span>"</span>)
        <span class="hljs-keyword">return</span> Money(self.amount - other.amount, self.currency)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__mul__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> isinstance(other, (int, float, Decimal)):
            <span class="hljs-keyword">return</span> Money(self.amount * Decimal(str(other)), self.currency)
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__truediv__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> isinstance(other, (int, float, Decimal)):
            <span class="hljs-keyword">return</span> Money(self.amount / Decimal(str(other)), self.currency)
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__eq__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(other, Money):
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>
        <span class="hljs-keyword">return</span> self.currency == other.currency <span class="hljs-keyword">and</span> self.amount == other.amount

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__lt__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(other, Money):
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>
        <span class="hljs-keyword">if</span> self.currency != other.currency:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Cannot compare different currencies: <span class="hljs-subst">{self.currency}</span> and <span class="hljs-subst">{other.currency}</span>"</span>)
        <span class="hljs-keyword">return</span> self.amount &lt; other.amount

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__str__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"<span class="hljs-subst">{self.currency}</span> <span class="hljs-subst">{self.amount:<span class="hljs-number">.2</span>f}</span>"</span>

    <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"Money(<span class="hljs-subst">{repr(float(self.amount))}</span>, <span class="hljs-subst">{repr(self.currency)}</span>)"</span>
</code></pre>
<p>Let's break down the key features of this <code>Money</code> class:</p>
<ol>
<li><p><strong>Precision handling</strong>: We use <code>Decimal</code> instead of <code>float</code> to avoid floating-point precision issues with money calculations.</p>
</li>
<li><p><strong>Currency safety</strong>: The class prevents operations between different currencies to avoid errors.</p>
</li>
<li><p><strong>Type checking</strong>: Each method checks if the other operand is of the correct type using <code>isinstance()</code>.</p>
</li>
<li><p><strong>NotImplemented</strong>: When an operation doesn't make sense, we return <code>NotImplemented</code> to let Python try the reverse operation.</p>
</li>
<li><p><strong>@total_ordering</strong>: This decorator automatically implements all comparison methods based on <code>__eq__</code> and <code>__lt__</code>.</p>
</li>
</ol>
<p>Here's how to use the <code>Money</code> class:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Basic arithmetic</span>
wallet = Money(<span class="hljs-number">100</span>, <span class="hljs-string">"USD"</span>)
expense = Money(<span class="hljs-number">20</span>, <span class="hljs-string">"USD"</span>)
remaining = wallet - expense
print(remaining)  <span class="hljs-comment"># Output: USD 80.00</span>

<span class="hljs-comment"># Working with different currencies</span>
salary = Money(<span class="hljs-number">5000</span>, <span class="hljs-string">"USD"</span>)
bonus = Money(<span class="hljs-number">1000</span>, <span class="hljs-string">"USD"</span>)
total = salary + bonus
print(total)  <span class="hljs-comment"># Output: USD 6000.00</span>

<span class="hljs-comment"># Division by scalar</span>
weekly_pay = salary / <span class="hljs-number">4</span>
print(weekly_pay)  <span class="hljs-comment"># Output: USD 1250.00</span>

<span class="hljs-comment"># Comparisons</span>
print(Money(<span class="hljs-number">100</span>, <span class="hljs-string">"USD"</span>) &gt; Money(<span class="hljs-number">50</span>, <span class="hljs-string">"USD"</span>))  <span class="hljs-comment"># Output: True</span>
print(Money(<span class="hljs-number">100</span>, <span class="hljs-string">"USD"</span>) == Money(<span class="hljs-number">100</span>, <span class="hljs-string">"USD"</span>))  <span class="hljs-comment"># Output: True</span>

<span class="hljs-comment"># Error handling</span>
<span class="hljs-keyword">try</span>:
    Money(<span class="hljs-number">100</span>, <span class="hljs-string">"USD"</span>) + Money(<span class="hljs-number">100</span>, <span class="hljs-string">"EUR"</span>)
<span class="hljs-keyword">except</span> ValueError <span class="hljs-keyword">as</span> e:
    print(e)  <span class="hljs-comment"># Output: Cannot add different currencies: USD and EUR</span>
</code></pre>
<p>This <code>Money</code> class demonstrates several important concepts:</p>
<ol>
<li><p>How to handle different types of operands</p>
</li>
<li><p>How to implement proper error handling</p>
</li>
<li><p>How to use the <code>@total_ordering</code> decorator</p>
</li>
<li><p>How to maintain precision in financial calculations</p>
</li>
<li><p>How to provide both string and representation methods</p>
</li>
</ol>
<h2 id="heading-container-methods"><strong>Container Methods</strong></h2>
<p>Container methods let you make your objects behave like built-in containers such as lists, dictionaries, or sets. This is particularly useful when you need custom behavior for storing and retrieving data.</p>
<h3 id="heading-sequence-protocol"><strong>Sequence Protocol</strong></h3>
<p>To make your object behave like a sequence (like a list or tuple), you need to implement these methods:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Method</td><td>Description</td><td>Example Usage</td></tr>
</thead>
<tbody>
<tr>
<td><code>__len__</code></td><td>Returns the length of the container</td><td><code>len(obj)</code></td></tr>
<tr>
<td><code>__getitem__</code></td><td>Allows indexing with <code>obj[key]</code></td><td><code>obj[0]</code></td></tr>
<tr>
<td><code>__setitem__</code></td><td>Allows assignment with <code>obj[key] = value</code></td><td><code>obj[0] = 42</code></td></tr>
<tr>
<td><code>__delitem__</code></td><td>Allows deletion with <code>del obj[key]</code></td><td><code>del obj[0]</code></td></tr>
<tr>
<td><code>__iter__</code></td><td>Returns an iterator for the container</td><td><code>for item in obj:</code></td></tr>
<tr>
<td><code>__contains__</code></td><td>Implements the <code>in</code> operator</td><td><code>42 in obj</code></td></tr>
</tbody>
</table>
</div><h3 id="heading-mapping-protocol"><strong>Mapping Protocol</strong></h3>
<p>For dictionary-like behavior, you'll want to implement these methods:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Method</td><td>Description</td><td>Example Usage</td></tr>
</thead>
<tbody>
<tr>
<td><code>__getitem__</code></td><td>Get value by key</td><td><code>obj["key"]</code></td></tr>
<tr>
<td><code>__setitem__</code></td><td>Set value by key</td><td><code>obj["key"] = value</code></td></tr>
<tr>
<td><code>__delitem__</code></td><td>Delete key-value pair</td><td><code>del obj["key"]</code></td></tr>
<tr>
<td><code>__len__</code></td><td>Get number of key-value pairs</td><td><code>len(obj)</code></td></tr>
<tr>
<td><code>__iter__</code></td><td>Iterate over keys</td><td><code>for key in obj:</code></td></tr>
<tr>
<td><code>__contains__</code></td><td>Check if key exists</td><td><code>"key" in obj</code></td></tr>
</tbody>
</table>
</div><h3 id="heading-practical-example-custom-cache"><strong>Practical Example: Custom Cache</strong></h3>
<p>Let's implement a time-based cache that automatically expires old entries. This example shows how to create a custom container that behaves like a dictionary but with additional functionality:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> time
<span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> OrderedDict

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExpiringCache</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, max_age_seconds=<span class="hljs-number">60</span></span>):</span>
        self.max_age = max_age_seconds
        self._cache = OrderedDict()  <span class="hljs-comment"># {key: (value, timestamp)}</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getitem__</span>(<span class="hljs-params">self, key</span>):</span>
        <span class="hljs-keyword">if</span> key <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> self._cache:
            <span class="hljs-keyword">raise</span> KeyError(key)

        value, timestamp = self._cache[key]
        <span class="hljs-keyword">if</span> time.time() - timestamp &gt; self.max_age:
            <span class="hljs-keyword">del</span> self._cache[key]
            <span class="hljs-keyword">raise</span> KeyError(<span class="hljs-string">f"Key '<span class="hljs-subst">{key}</span>' has expired"</span>)

        <span class="hljs-keyword">return</span> value

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__setitem__</span>(<span class="hljs-params">self, key, value</span>):</span>
        self._cache[key] = (value, time.time())
        self._cache.move_to_end(key)  <span class="hljs-comment"># Move to end to maintain insertion order</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__delitem__</span>(<span class="hljs-params">self, key</span>):</span>
        <span class="hljs-keyword">del</span> self._cache[key]

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__len__</span>(<span class="hljs-params">self</span>):</span>
        self._clean_expired()  <span class="hljs-comment"># Clean expired items before reporting length</span>
        <span class="hljs-keyword">return</span> len(self._cache)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__iter__</span>(<span class="hljs-params">self</span>):</span>
        self._clean_expired()  <span class="hljs-comment"># Clean expired items before iteration</span>
        <span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> self._cache:
            <span class="hljs-keyword">yield</span> key

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__contains__</span>(<span class="hljs-params">self, key</span>):</span>
        <span class="hljs-keyword">if</span> key <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> self._cache:
            <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>

        _, timestamp = self._cache[key]
        <span class="hljs-keyword">if</span> time.time() - timestamp &gt; self.max_age:
            <span class="hljs-keyword">del</span> self._cache[key]
            <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-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_clean_expired</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Remove all expired entries from the cache."""</span>
        now = time.time()
        expired_keys = [
            key <span class="hljs-keyword">for</span> key, (_, timestamp) <span class="hljs-keyword">in</span> self._cache.items()
            <span class="hljs-keyword">if</span> now - timestamp &gt; self.max_age
        ]
        <span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> expired_keys:
            <span class="hljs-keyword">del</span> self._cache[key]
</code></pre>
<p>Let's break down how this cache works:</p>
<ol>
<li><p><strong>Storage</strong>: The cache uses an <code>OrderedDict</code> to store key-value pairs along with timestamps.</p>
</li>
<li><p><strong>Expiration</strong>: Each value is stored as a tuple of <code>(value, timestamp)</code>. When accessing a value, we check if it has expired.</p>
</li>
<li><p><strong>Container methods</strong>: The class implements all necessary methods to behave like a dictionary:</p>
<ul>
<li><p><code>__getitem__</code>: Retrieves values and checks expiration</p>
</li>
<li><p><code>__setitem__</code>: Stores values with current timestamp</p>
</li>
<li><p><code>__delitem__</code>: Removes entries</p>
</li>
<li><p><code>__len__</code>: Returns number of non-expired entries</p>
</li>
<li><p><code>__iter__</code>: Iterates over non-expired keys</p>
</li>
<li><p><code>__contains__</code>: Checks if a key exists</p>
</li>
</ul>
</li>
</ol>
<p>Here's how to use the cache:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create a cache with 2-second expiration</span>
cache = ExpiringCache(max_age_seconds=<span class="hljs-number">2</span>)

<span class="hljs-comment"># Store some values</span>
cache[<span class="hljs-string">"name"</span>] = <span class="hljs-string">"Vivek"</span>
cache[<span class="hljs-string">"age"</span>] = <span class="hljs-number">30</span>

<span class="hljs-comment"># Access values</span>
print(<span class="hljs-string">"name"</span> <span class="hljs-keyword">in</span> cache)  <span class="hljs-comment"># Output: True</span>
print(cache[<span class="hljs-string">"name"</span>])    <span class="hljs-comment"># Output: Vivek</span>
print(len(cache))       <span class="hljs-comment"># Output: 2</span>

<span class="hljs-comment"># Wait for expiration</span>
print(<span class="hljs-string">"Waiting for expiration..."</span>)
time.sleep(<span class="hljs-number">3</span>)

<span class="hljs-comment"># Check expired values</span>
print(<span class="hljs-string">"name"</span> <span class="hljs-keyword">in</span> cache)  <span class="hljs-comment"># Output: False</span>
<span class="hljs-keyword">try</span>:
    print(cache[<span class="hljs-string">"name"</span>])
<span class="hljs-keyword">except</span> KeyError <span class="hljs-keyword">as</span> e:
    print(<span class="hljs-string">f"KeyError: <span class="hljs-subst">{e}</span>"</span>)  <span class="hljs-comment"># Output: KeyError: 'name'</span>

print(len(cache))  <span class="hljs-comment"># Output: 0</span>
</code></pre>
<p>This cache implementation provides several benefits:</p>
<ol>
<li><p>Automatic expiration of old entries</p>
</li>
<li><p>Dictionary-like interface for easy use</p>
</li>
<li><p>Memory efficiency by removing expired entries</p>
</li>
<li><p>Thread-safe operations (assuming single-threaded access)</p>
</li>
<li><p>Maintains insertion order of entries</p>
</li>
</ol>
<h2 id="heading-attribute-access"><strong>Attribute Access</strong></h2>
<p>Attribute access methods let you control how your objects handle getting, setting, and deleting attributes. This is particularly useful for implementing properties, validation, and logging.</p>
<h3 id="heading-getattr-and-getattribute"><strong>getattr and getattribute</strong></h3>
<p>Python provides two methods for controlling attribute access:</p>
<ol>
<li><p><code>__getattr__</code>: Called only when an attribute lookup fails (that is, when the attribute doesn't exist)</p>
</li>
<li><p><code>__getattribute__</code>: Called for every attribute access, even for attributes that exist</p>
</li>
</ol>
<p>The key difference is that <code>__getattribute__</code> is called for all attribute access, while <code>__getattr__</code> is only called when the attribute isn't found through normal means.</p>
<p>Here's a simple example showing the difference:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AttributeDemo</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>
        self.name = <span class="hljs-string">"Vivek"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getattr__</span>(<span class="hljs-params">self, name</span>):</span>
        print(<span class="hljs-string">f"__getattr__ called for <span class="hljs-subst">{name}</span>"</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Default value for <span class="hljs-subst">{name}</span>"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getattribute__</span>(<span class="hljs-params">self, name</span>):</span>
        print(<span class="hljs-string">f"__getattribute__ called for <span class="hljs-subst">{name}</span>"</span>)
        <span class="hljs-keyword">return</span> super().__getattribute__(name)

demo = AttributeDemo()
print(demo.name)      <span class="hljs-comment"># Output: __getattribute__ called for name</span>
                      <span class="hljs-comment">#        Vivek</span>
print(demo.age)       <span class="hljs-comment"># Output: __getattribute__ called for age</span>
                      <span class="hljs-comment">#        __getattr__ called for age</span>
                      <span class="hljs-comment">#        Default value for age</span>
</code></pre>
<h3 id="heading-setattr-and-delattr"><strong>setattr and delattr</strong></h3>
<p>Similarly, you can control how attributes are set and deleted:</p>
<ol>
<li><p><code>__setattr__</code>: Called when an attribute is set</p>
</li>
<li><p><code>__delattr__</code>: Called when an attribute is deleted</p>
</li>
</ol>
<p>These methods let you implement validation, logging, or custom behavior when attributes are modified.</p>
<h3 id="heading-practical-example-auto-logging-properties"><strong>Practical Example: Auto-Logging Properties</strong></h3>
<p>Let's create a class that automatically logs all property changes. This is useful for debugging, auditing, or tracking object state changes:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> logging

<span class="hljs-comment"># Set up logging</span>
logging.basicConfig(
    level=logging.INFO,
    format=<span class="hljs-string">'%(asctime)s - %(levelname)s - %(message)s'</span>
)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LoggedObject</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, **kwargs</span>):</span>
        self._data = {}
        <span class="hljs-comment"># Initialize attributes without triggering __setattr__</span>
        <span class="hljs-keyword">for</span> key, value <span class="hljs-keyword">in</span> kwargs.items():
            self._data[key] = value

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getattr__</span>(<span class="hljs-params">self, name</span>):</span>
        <span class="hljs-keyword">if</span> name <span class="hljs-keyword">in</span> self._data:
            logging.debug(<span class="hljs-string">f"Accessing attribute <span class="hljs-subst">{name}</span>: <span class="hljs-subst">{self._data[name]}</span>"</span>)
            <span class="hljs-keyword">return</span> self._data[name]
        <span class="hljs-keyword">raise</span> AttributeError(<span class="hljs-string">f"'<span class="hljs-subst">{self.__class__.__name__}</span>' object has no attribute '<span class="hljs-subst">{name}</span>'"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__setattr__</span>(<span class="hljs-params">self, name, value</span>):</span>
        <span class="hljs-keyword">if</span> name == <span class="hljs-string">"_data"</span>:
            <span class="hljs-comment"># Allow setting the _data attribute directly</span>
            super().__setattr__(name, value)
        <span class="hljs-keyword">else</span>:
            old_value = self._data.get(name, <span class="hljs-string">"&lt;undefined&gt;"</span>)
            self._data[name] = value
            logging.info(<span class="hljs-string">f"Changed <span class="hljs-subst">{name}</span>: <span class="hljs-subst">{old_value}</span> -&gt; <span class="hljs-subst">{value}</span>"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__delattr__</span>(<span class="hljs-params">self, name</span>):</span>
        <span class="hljs-keyword">if</span> name <span class="hljs-keyword">in</span> self._data:
            old_value = self._data[name]
            <span class="hljs-keyword">del</span> self._data[name]
            logging.info(<span class="hljs-string">f"Deleted <span class="hljs-subst">{name}</span> (was: <span class="hljs-subst">{old_value}</span>)"</span>)
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">raise</span> AttributeError(<span class="hljs-string">f"'<span class="hljs-subst">{self.__class__.__name__}</span>' object has no attribute '<span class="hljs-subst">{name}</span>'"</span>)
</code></pre>
<p>Let's break down how this class works:</p>
<ol>
<li><p><strong>Storage</strong>: The class uses a private <code>_data</code> dictionary to store attribute values.</p>
</li>
<li><p><strong>Attribute access</strong>:</p>
<ul>
<li><p><code>__getattr__</code>: Returns values from <code>_data</code> and logs debug messages</p>
</li>
<li><p><code>__setattr__</code>: Stores values in <code>_data</code> and logs changes</p>
</li>
<li><p><code>__delattr__</code>: Removes values from <code>_data</code> and logs deletions</p>
</li>
</ul>
</li>
<li><p><strong>Special handling</strong>: The <code>_data</code> attribute itself is handled differently to avoid infinite recursion.</p>
</li>
</ol>
<p>Here's how to use the class:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create a logged object with initial values</span>
user = LoggedObject(name=<span class="hljs-string">"Vivek"</span>, email=<span class="hljs-string">"hello@wewake.dev"</span>)

<span class="hljs-comment"># Modify attributes</span>
user.name = <span class="hljs-string">"Vivek"</span>  <span class="hljs-comment"># Logs: Changed name: Vivek -&gt; Vivek</span>
user.age = <span class="hljs-number">30</span>         <span class="hljs-comment"># Logs: Changed age: &lt;undefined&gt; -&gt; 30</span>

<span class="hljs-comment"># Access attributes</span>
print(user.name)      <span class="hljs-comment"># Output: Vivek</span>

<span class="hljs-comment"># Delete attributes</span>
<span class="hljs-keyword">del</span> user.email        <span class="hljs-comment"># Logs: Deleted email (was: hello@wewake.dev)</span>

<span class="hljs-comment"># Try to access deleted attribute</span>
<span class="hljs-keyword">try</span>:
    print(user.email)
<span class="hljs-keyword">except</span> AttributeError <span class="hljs-keyword">as</span> e:
    print(<span class="hljs-string">f"AttributeError: <span class="hljs-subst">{e}</span>"</span>)  <span class="hljs-comment"># Output: AttributeError: 'LoggedObject' object has no attribute 'email'</span>
</code></pre>
<p>This implementation provides several benefits:</p>
<ol>
<li><p>Automatic logging of all attribute changes</p>
</li>
<li><p>Debug-level logging for attribute access</p>
</li>
<li><p>Clear error messages for missing attributes</p>
</li>
<li><p>Easy tracking of object state changes</p>
</li>
<li><p>Useful for debugging and auditing</p>
</li>
</ol>
<h2 id="heading-context-managers"><strong>Context Managers</strong></h2>
<p>Context managers are a powerful feature in Python that helps you manage resources properly. They ensure that resources are properly acquired and released, even if an error occurs. The <code>with</code> statement is the most common way to use context managers.</p>
<h3 id="heading-enter-and-exit"><strong>enter and exit</strong></h3>
<p>To create a context manager, you need to implement two magic methods:</p>
<ol>
<li><p><code>__enter__</code>: Called when entering the <code>with</code> block. It should return the resource to be managed.</p>
</li>
<li><p><code>__exit__</code>: Called when exiting the <code>with</code> block, even if an exception occurs. It should handle cleanup.</p>
</li>
</ol>
<p>The <code>__exit__</code> method receives three arguments:</p>
<ul>
<li><p><code>exc_type</code>: The type of the exception (if any)</p>
</li>
<li><p><code>exc_val</code>: The exception instance (if any)</p>
</li>
<li><p><code>exc_tb</code>: The traceback (if any)</p>
</li>
</ul>
<h3 id="heading-practical-example-database-connection-manager"><strong>Practical Example: Database Connection Manager</strong></h3>
<p>Let's create a context manager for database connections. This example shows how to properly manage database resources and handle transactions:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3
<span class="hljs-keyword">import</span> logging

<span class="hljs-comment"># Set up logging</span>
logging.basicConfig(
    level=logging.INFO,
    format=<span class="hljs-string">'%(asctime)s - %(levelname)s - %(message)s'</span>
)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DatabaseConnection</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, db_path</span>):</span>
        self.db_path = db_path
        self.connection = <span class="hljs-literal">None</span>
        self.cursor = <span class="hljs-literal">None</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__enter__</span>(<span class="hljs-params">self</span>):</span>
        logging.info(<span class="hljs-string">f"Connecting to database: <span class="hljs-subst">{self.db_path}</span>"</span>)
        self.connection = sqlite3.connect(self.db_path)
        self.cursor = self.connection.cursor()
        <span class="hljs-keyword">return</span> self.cursor

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__exit__</span>(<span class="hljs-params">self, exc_type, exc_val, exc_tb</span>):</span>
        <span class="hljs-keyword">if</span> exc_type <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span>:
            logging.error(<span class="hljs-string">f"An error occurred: <span class="hljs-subst">{exc_val}</span>"</span>)
            self.connection.rollback()
            logging.info(<span class="hljs-string">"Transaction rolled back"</span>)
        <span class="hljs-keyword">else</span>:
            self.connection.commit()
            logging.info(<span class="hljs-string">"Transaction committed"</span>)

        <span class="hljs-keyword">if</span> self.cursor:
            self.cursor.close()
        <span class="hljs-keyword">if</span> self.connection:
            self.connection.close()

        logging.info(<span class="hljs-string">"Database connection closed"</span>)

        <span class="hljs-comment"># Return False to propagate exceptions, True to suppress them</span>
        <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
</code></pre>
<p>Let's break down how this context manager works:</p>
<ol>
<li><p><strong>Initialization</strong>:</p>
<ul>
<li><p>The class takes a database path</p>
</li>
<li><p>It initializes connection and cursor as None</p>
</li>
</ul>
</li>
<li><p><strong>Enter method</strong>:</p>
<ul>
<li><p>Creates a database connection</p>
</li>
<li><p>Creates a cursor</p>
</li>
<li><p>Returns the cursor for use in the <code>with</code> block</p>
</li>
</ul>
</li>
<li><p><strong>Exit method</strong>:</p>
<ul>
<li><p>Handles transaction management (commit/rollback)</p>
</li>
<li><p>Closes cursor and connection</p>
</li>
<li><p>Logs all operations</p>
</li>
<li><p>Returns False to propagate exceptions</p>
</li>
</ul>
</li>
</ol>
<p>Here's how to use the context manager:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create a test database in memory</span>
<span class="hljs-keyword">try</span>:
    <span class="hljs-comment"># Successful transaction</span>
    <span class="hljs-keyword">with</span> DatabaseConnection(<span class="hljs-string">":memory:"</span>) <span class="hljs-keyword">as</span> cursor:
        <span class="hljs-comment"># Create a table</span>
        cursor.execute(<span class="hljs-string">"""
            CREATE TABLE users (
                id INTEGER PRIMARY KEY,
                name TEXT,
                email TEXT
            )
        """</span>)

        <span class="hljs-comment"># Insert data</span>
        cursor.execute(
            <span class="hljs-string">"INSERT INTO users (name, email) VALUES (?, ?)"</span>,
            (<span class="hljs-string">"Vivek"</span>, <span class="hljs-string">"hello@wewake.dev"</span>)
        )

        <span class="hljs-comment"># Query data</span>
        cursor.execute(<span class="hljs-string">"SELECT * FROM users"</span>)
        print(cursor.fetchall())  <span class="hljs-comment"># Output: [(1, 'Vivek', 'hello@wewake.dev')]</span>

    <span class="hljs-comment"># Demonstrate transaction rollback on error</span>
    <span class="hljs-keyword">with</span> DatabaseConnection(<span class="hljs-string">":memory:"</span>) <span class="hljs-keyword">as</span> cursor:
        cursor.execute(<span class="hljs-string">"""
            CREATE TABLE users (
                id INTEGER PRIMARY KEY,
                name TEXT,
                email TEXT
            )
        """</span>)
        cursor.execute(
            <span class="hljs-string">"INSERT INTO users (name, email) VALUES (?, ?)"</span>,
            (<span class="hljs-string">"Wewake"</span>, <span class="hljs-string">"contact@wewake.dev"</span>)
        )
        <span class="hljs-comment"># This will cause an error - table 'nonexistent' doesn't exist</span>
        cursor.execute(<span class="hljs-string">"SELECT * FROM nonexistent"</span>)
<span class="hljs-keyword">except</span> sqlite3.OperationalError <span class="hljs-keyword">as</span> e:
    print(<span class="hljs-string">f"Caught exception: <span class="hljs-subst">{e}</span>"</span>)
</code></pre>
<p>This context manager provides several benefits:</p>
<ol>
<li><p>Resources are managed automatically (ex: connections are always closed).</p>
</li>
<li><p>With transaction safety, changes are committed or rolled back appropriately.</p>
</li>
<li><p>Exceptions are caught and handled gracefully</p>
</li>
<li><p>All operations are logged for debugging</p>
</li>
<li><p>The <code>with</code> statement makes the code clear and concise</p>
</li>
</ol>
<h2 id="heading-callable-objects"><strong>Callable Objects</strong></h2>
<p>The <code>__call__</code> magic method lets you make instances of your class behave like functions. This is useful for creating objects that maintain state between calls or for implementing function-like behavior with additional features.</p>
<h3 id="heading-call"><strong>call</strong></h3>
<p>The <code>__call__</code> method is called when you try to call an instance of your class as if it were a function. Here's a simple example:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Multiplier</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, factor</span>):</span>
        self.factor = factor

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__call__</span>(<span class="hljs-params">self, x</span>):</span>
        <span class="hljs-keyword">return</span> x * self.factor

<span class="hljs-comment"># Create instances that behave like functions</span>
double = Multiplier(<span class="hljs-number">2</span>)
triple = Multiplier(<span class="hljs-number">3</span>)

print(double(<span class="hljs-number">5</span>))  <span class="hljs-comment"># Output: 10</span>
print(triple(<span class="hljs-number">5</span>))  <span class="hljs-comment"># Output: 15</span>
</code></pre>
<p>This example shows how <code>__call__</code> lets you create objects that maintain state (the factor) while being callable like functions.</p>
<h3 id="heading-practical-example-memoization-decorator"><strong>Practical Example: Memoization Decorator</strong></h3>
<p>Let's implement a memoization decorator using <code>__call__</code>. This decorator will cache function results to avoid redundant computations:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> time
<span class="hljs-keyword">import</span> functools

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Memoize</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, func</span>):</span>
        self.func = func
        self.cache = {}
        <span class="hljs-comment"># Preserve function metadata (name, docstring, etc.)</span>
        functools.update_wrapper(self, func)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__call__</span>(<span class="hljs-params">self, *args, **kwargs</span>):</span>
        <span class="hljs-comment"># Create a key from the arguments</span>
        <span class="hljs-comment"># For simplicity, we assume all arguments are hashable</span>
        key = str(args) + str(sorted(kwargs.items()))

        <span class="hljs-keyword">if</span> key <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> self.cache:
            self.cache[key] = self.func(*args, **kwargs)

        <span class="hljs-keyword">return</span> self.cache[key]

<span class="hljs-comment"># Usage</span>
<span class="hljs-meta">@Memoize</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">fibonacci</span>(<span class="hljs-params">n</span>):</span>
    <span class="hljs-string">"""Calculate the nth Fibonacci number recursively."""</span>
    <span class="hljs-keyword">if</span> n &lt;= <span class="hljs-number">1</span>:
        <span class="hljs-keyword">return</span> n
    <span class="hljs-keyword">return</span> fibonacci(n<span class="hljs-number">-1</span>) + fibonacci(n<span class="hljs-number">-2</span>)

<span class="hljs-comment"># Measure execution time</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">time_execution</span>(<span class="hljs-params">func, *args, **kwargs</span>):</span>
    start = time.time()
    result = func(*args, **kwargs)
    end = time.time()
    print(<span class="hljs-string">f"<span class="hljs-subst">{func.__name__}</span>(<span class="hljs-subst">{args}</span>, <span class="hljs-subst">{kwargs}</span>) took <span class="hljs-subst">{end - start:<span class="hljs-number">.6</span>f}</span> seconds"</span>)
    <span class="hljs-keyword">return</span> result

<span class="hljs-comment"># Without memoization, this would be extremely slow</span>
print(<span class="hljs-string">"Calculating fibonacci(35)..."</span>)
result = time_execution(fibonacci, <span class="hljs-number">35</span>)
print(<span class="hljs-string">f"Result: <span class="hljs-subst">{result}</span>"</span>)

<span class="hljs-comment"># Second call is instant due to memoization</span>
print(<span class="hljs-string">"\nCalculating fibonacci(35) again..."</span>)
result = time_execution(fibonacci, <span class="hljs-number">35</span>)
print(<span class="hljs-string">f"Result: <span class="hljs-subst">{result}</span>"</span>)
</code></pre>
<p>Let's break down how this memoization decorator works:</p>
<ol>
<li><p><strong>Initialization</strong>:</p>
<ul>
<li><p>Takes a function as an argument</p>
</li>
<li><p>Creates a cache dictionary to store results</p>
</li>
<li><p>Preserves the function's metadata using <code>functools.update_wrapper</code></p>
</li>
</ul>
</li>
<li><p><strong>Call method</strong>:</p>
<ul>
<li><p>Creates a unique key from the function arguments</p>
</li>
<li><p>Checks if the result is in the cache</p>
</li>
<li><p>If not, computes the result and stores it</p>
</li>
<li><p>Returns the cached result</p>
</li>
</ul>
</li>
<li><p><strong>Usage</strong>:</p>
<ul>
<li><p>Applied as a decorator to any function</p>
</li>
<li><p>Automatically caches results for repeated calls</p>
</li>
<li><p>Preserves function metadata and behavior</p>
</li>
</ul>
</li>
</ol>
<p>The benefits of this implementation include:</p>
<ol>
<li><p>Better performance, as it avoids redundant computations</p>
</li>
<li><p>Better, transparency, as it works without modifying the original function</p>
</li>
<li><p>It’s flexible, and can be used with any function</p>
</li>
<li><p>It’s memory efficient and caches results for reuse</p>
</li>
<li><p>It maintains function documentation</p>
</li>
</ol>
<h2 id="heading-advanced-magic-methods"><strong>Advanced Magic Methods</strong></h2>
<p>Now let's explore some of Python's more advanced magic methods. These methods give you fine-grained control over object creation, memory usage, and dictionary behavior.</p>
<h3 id="heading-new-for-object-creation"><strong>new for Object Creation</strong></h3>
<p>The <code>__new__</code> method is called before <code>__init__</code> and is responsible for creating and returning a new instance of the class. This is useful for implementing patterns like singletons or immutable objects.</p>
<p>Here's an example of a singleton pattern using <code>__new__</code>:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Singleton</span>:</span>
    _instance = <span class="hljs-literal">None</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__new__</span>(<span class="hljs-params">cls, *args, **kwargs</span>):</span>
        <span class="hljs-keyword">if</span> cls._instance <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
            cls._instance = super().__new__(cls)
        <span class="hljs-keyword">return</span> cls._instance

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, name=None</span>):</span>
        <span class="hljs-comment"># This will be called every time Singleton() is called</span>
        <span class="hljs-keyword">if</span> name <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span>:
            self.name = name

<span class="hljs-comment"># Usage</span>
s1 = Singleton(<span class="hljs-string">"Vivek"</span>)
s2 = Singleton(<span class="hljs-string">"Wewake"</span>)
print(s1 <span class="hljs-keyword">is</span> s2)  <span class="hljs-comment"># Output: True</span>
print(s1.name)   <span class="hljs-comment"># Output: Wewake (the second initialization overwrote the first)</span>
</code></pre>
<p>Let's break down how this singleton works:</p>
<ol>
<li><p><strong>Class variable</strong>: <code>_instance</code> stores the single instance of the class</p>
</li>
<li><p><strong>new</strong> method:</p>
<ul>
<li><p>Checks if an instance exists</p>
</li>
<li><p>Creates one if it doesn't</p>
</li>
<li><p>Returns the existing instance if it does</p>
</li>
</ul>
</li>
<li><p><strong>init</strong> method:</p>
<ul>
<li><p>Called every time the constructor is used</p>
</li>
<li><p>Updates the instance's attributes</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-slots-for-memory-optimization"><strong>slots for Memory Optimization</strong></h3>
<p>The <code>__slots__</code> class variable restricts which attributes an instance can have, saving memory. This is particularly useful when you have many instances of a class with a fixed set of attributes.</p>
<p>Here's a comparison of regular and slotted classes:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sys

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RegularPerson</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, email</span>):</span>
        self.name = name
        self.age = age
        self.email = email

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SlottedPerson</span>:</span>
    __slots__ = [<span class="hljs-string">'name'</span>, <span class="hljs-string">'age'</span>, <span class="hljs-string">'email'</span>]

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

<span class="hljs-comment"># Compare memory usage</span>
regular_people = [RegularPerson(<span class="hljs-string">"Vivek"</span> + str(i), <span class="hljs-number">30</span>, <span class="hljs-string">"hello@wewake.dev"</span>) <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">1000</span>)]
slotted_people = [SlottedPerson(<span class="hljs-string">"Vivek"</span> + str(i), <span class="hljs-number">30</span>, <span class="hljs-string">"hello@wewake.dev"</span>) <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">1000</span>)]

print(<span class="hljs-string">f"Regular person size: <span class="hljs-subst">{sys.getsizeof(regular_people[<span class="hljs-number">0</span>])}</span> bytes"</span>)  <span class="hljs-comment"># Output: Regular person size: 48 bytes</span>
print(<span class="hljs-string">f"Slotted person size: <span class="hljs-subst">{sys.getsizeof(slotted_people[<span class="hljs-number">0</span>])}</span> bytes"</span>)  <span class="hljs-comment"># Output: Slotted person size: 56 bytes</span>
print(<span class="hljs-string">f"Memory saved per instance: <span class="hljs-subst">{sys.getsizeof(regular_people[<span class="hljs-number">0</span>]) - sys.getsizeof(slotted_people[<span class="hljs-number">0</span>])}</span> bytes"</span>)  <span class="hljs-comment"># Output: Memory saved per instance: -8 bytes</span>
print(<span class="hljs-string">f"Total memory saved for 1000 instances: <span class="hljs-subst">{(sys.getsizeof(regular_people[<span class="hljs-number">0</span>]) - sys.getsizeof(slotted_people[<span class="hljs-number">0</span>])) * <span class="hljs-number">1000</span> / <span class="hljs-number">1024</span>:<span class="hljs-number">.2</span>f}</span> KB"</span>)  <span class="hljs-comment"># Output: Total memory saved for 1000 instances: -7.81 KB</span>
</code></pre>
<p>Running this code produces an interesting result:</p>
<pre><code class="lang-plaintext">Regular person size: 48 bytes
Slotted person size: 56 bytes
Memory saved per instance: -8 bytes
Total memory saved for 1000 instances: -7.81 KB
</code></pre>
<p>Surprisingly, in this simple example, the slotted instance is actually 8 bytes larger than the regular instance! This seems to contradict the common advice about <code>__slots__</code> saving memory.</p>
<p>So what's going on here? The real memory savings from <code>__slots__</code> come from:</p>
<ol>
<li><p>Eliminating dictionaries: Regular Python objects store their attributes in a dictionary (<code>__dict__</code>), which has overhead. The <code>sys.getsizeof()</code> function doesn't account for this dictionary's size.</p>
</li>
<li><p>Storing attributes: For small objects with few attributes, the overhead of the slot descriptors can outweigh the dictionary savings.</p>
</li>
<li><p>Scalability: The real benefit appears when:</p>
<ul>
<li><p>You have many instances (thousands or millions)</p>
</li>
<li><p>Your objects have many attributes</p>
</li>
<li><p>You're adding attributes dynamically</p>
</li>
</ul>
</li>
</ol>
<p>Let's see a more complete comparison:</p>
<pre><code class="lang-python"><span class="hljs-comment"># A more accurate memory measurement</span>
<span class="hljs-keyword">import</span> sys

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_size</span>(<span class="hljs-params">obj</span>):</span>
    <span class="hljs-string">"""Get a better estimate of the object's size in bytes."""</span>
    size = sys.getsizeof(obj)
    <span class="hljs-keyword">if</span> hasattr(obj, <span class="hljs-string">'__dict__'</span>):
        size += sys.getsizeof(obj.__dict__)
        <span class="hljs-comment"># Add the size of the dict contents</span>
        size += sum(sys.getsizeof(v) <span class="hljs-keyword">for</span> v <span class="hljs-keyword">in</span> obj.__dict__.values())
    <span class="hljs-keyword">return</span> size

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RegularPerson</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, email</span>):</span>
        self.name = name
        self.age = age
        self.email = email

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SlottedPerson</span>:</span>
    __slots__ = [<span class="hljs-string">'name'</span>, <span class="hljs-string">'age'</span>, <span class="hljs-string">'email'</span>]

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

regular = RegularPerson(<span class="hljs-string">"Vivek"</span>, <span class="hljs-number">30</span>, <span class="hljs-string">"hello@wewake.dev"</span>)
slotted = SlottedPerson(<span class="hljs-string">"Vivek"</span>, <span class="hljs-number">30</span>, <span class="hljs-string">"hello@wewake.dev"</span>)

print(<span class="hljs-string">f"Complete Regular person size: <span class="hljs-subst">{get_size(regular)}</span> bytes"</span>)  <span class="hljs-comment"># Output: Complete Regular person size: 610 bytes</span>
print(<span class="hljs-string">f"Complete Slotted person size: <span class="hljs-subst">{get_size(slotted)}</span> bytes"</span>)  <span class="hljs-comment"># Output: Complete Slotted person size: 56 bytes</span>
</code></pre>
<p>With this more accurate measurement, you'll see that slotted objects typically use less total memory, especially as you add more attributes.</p>
<p>Key points about <code>__slots__</code>:</p>
<ol>
<li><p><strong>Real memory benefits</strong>: The primary memory savings come from eliminating the instance <code>__dict__</code></p>
</li>
<li><p><strong>Dynamic restrictions</strong>: You can't add arbitrary attributes to slotted objects</p>
</li>
<li><p><strong>Inheritance considerations</strong>: Using <code>__slots__</code> with inheritance requires careful planning</p>
</li>
<li><p><strong>Use cases</strong>: Best for classes with many instances and fixed attributes</p>
</li>
<li><p><strong>Performance bonus</strong>: Can also provide faster attribute access in some cases</p>
</li>
</ol>
<h3 id="heading-missing-for-default-dictionary-values"><strong>missing for Default Dictionary Values</strong></h3>
<p>The <code>__missing__</code> method is called by dictionary subclasses when a key is not found. This is useful for implementing dictionaries with default values or automatic key creation.</p>
<p>Here's an example of a dictionary that automatically creates empty lists for missing keys:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AutoKeyDict</span>(<span class="hljs-params">dict</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__missing__</span>(<span class="hljs-params">self, key</span>):</span>
        self[key] = []
        <span class="hljs-keyword">return</span> self[key]

<span class="hljs-comment"># Usage</span>
groups = AutoKeyDict()
groups[<span class="hljs-string">"team1"</span>].append(<span class="hljs-string">"Vivek"</span>)
groups[<span class="hljs-string">"team1"</span>].append(<span class="hljs-string">"Wewake"</span>)
groups[<span class="hljs-string">"team2"</span>].append(<span class="hljs-string">"Vibha"</span>)

print(groups)  <span class="hljs-comment"># Output: {'team1': ['Vivek', 'Wewake'], 'team2': ['Vibha']}</span>
</code></pre>
<p>This implementation provides several benefits:</p>
<ol>
<li><p>No need to check if a key exists, which is more convenient.</p>
</li>
<li><p>Automatic initialization creates default values as needed.</p>
</li>
<li><p>Reduces boilerplate for dictionary initialization.</p>
</li>
<li><p>It’s more flexible, and can implement any default value logic.</p>
</li>
<li><p>Only creates values when needed, making it more memory efficient.</p>
</li>
</ol>
<h2 id="heading-performance-considerations"><strong>Performance Considerations</strong></h2>
<p>While magic methods are powerful, they can impact performance if you don’t use them carefully. Let's explore some common performance considerations and how to measure them.</p>
<h3 id="heading-impact-of-magic-methods-on-performance"><strong>Impact of Magic Methods on Performance</strong></h3>
<p>Different magic methods have different performance implications:</p>
<p><strong>Attribute Access methods</strong>:</p>
<ul>
<li><p><code>__getattr__</code>, <code>__getattribute__</code>, <code>__setattr__</code>, and <code>__delattr__</code> are called frequently</p>
</li>
<li><p>Complex operations in these methods can significantly slow down your code</p>
</li>
</ul>
<p><strong>Container methods</strong>:</p>
<ul>
<li><p><code>__getitem__</code>, <code>__setitem__</code>, and <code>__len__</code> are called often in loops</p>
</li>
<li><p>Inefficient implementations can make your container much slower than built-in types</p>
</li>
</ul>
<p><strong>Operator overloading</strong>:</p>
<ul>
<li><p>Arithmetic and comparison operators are used frequently</p>
</li>
<li><p>Complex implementations can make simple operations unexpectedly slow</p>
</li>
</ul>
<p>Let's measure the performance impact of <code>__getattr__</code> vs. direct attribute access:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> time

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DirectAccess</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>
        self.value = <span class="hljs-number">42</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GetAttrAccess</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>
        self._value = <span class="hljs-number">42</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getattr__</span>(<span class="hljs-params">self, name</span>):</span>
        <span class="hljs-keyword">if</span> name == <span class="hljs-string">"value"</span>:
            <span class="hljs-keyword">return</span> self._value
        <span class="hljs-keyword">raise</span> AttributeError(<span class="hljs-string">f"'<span class="hljs-subst">{self.__class__.__name__}</span>' object has no attribute '<span class="hljs-subst">{name}</span>'"</span>)

<span class="hljs-comment"># Measure performance</span>
direct = DirectAccess()
getattr_obj = GetAttrAccess()

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">benchmark</span>(<span class="hljs-params">obj, iterations=<span class="hljs-number">1000000</span></span>):</span>
    start = time.time()
    <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(iterations):
        x = obj.value
    end = time.time()
    <span class="hljs-keyword">return</span> end - start

direct_time = benchmark(direct)
getattr_time = benchmark(getattr_obj)

print(<span class="hljs-string">f"Direct access: <span class="hljs-subst">{direct_time:<span class="hljs-number">.6</span>f}</span> seconds"</span>)
print(<span class="hljs-string">f"__getattr__ access: <span class="hljs-subst">{getattr_time:<span class="hljs-number">.6</span>f}</span> seconds"</span>)
print(<span class="hljs-string">f"__getattr__ is <span class="hljs-subst">{getattr_time / direct_time:<span class="hljs-number">.2</span>f}</span>x slower"</span>)
</code></pre>
<p>Running this benchmark shows significant performance differences:</p>
<pre><code class="lang-plaintext">Direct access: 0.027714 seconds
__getattr__ access: 0.060646 seconds
__getattr__ is 2.19x slower
</code></pre>
<p>As you can see, using <code>__getattr__</code> is more than twice as slow as direct attribute access. This might not matter for occasionally accessed attributes, but it can become significant in performance-critical code that accesses attributes in tight loops.</p>
<h3 id="heading-optimization-strategies"><strong>Optimization Strategies</strong></h3>
<p>Fortunately, there are various ways you can optimize magic methods.</p>
<ol>
<li><p><strong>Use slots for memory efficiency</strong>: This reduces memory usage and improves attribute access speed. It’s best for classes with many instances.</p>
</li>
<li><p><strong>Cache computed values</strong>: You can store results of expensive operations and update the cache only when necessary. Use <code>@property</code> for computed attributes.</p>
</li>
<li><p><strong>Minimize method calls</strong>: Make sure you avoid unnecessary magic method calls and use direct attribute access when possible. Consider using <code>__slots__</code> for frequently accessed attributes.</p>
</li>
</ol>
<h2 id="heading-best-practices"><strong>Best Practices</strong></h2>
<p>When using magic methods, follow these best practices to ensure your code is maintainable, efficient, and reliable.</p>
<h3 id="heading-1-be-consistent"><strong>1. Be Consistent</strong></h3>
<p>When implementing related magic methods, maintain consistency in behavior:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> functools <span class="hljs-keyword">import</span> total_ordering

<span class="hljs-meta">@total_ordering</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConsistentNumber</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, value</span>):</span>
        self.value = value

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__eq__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(other, ConsistentNumber):
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>
        <span class="hljs-keyword">return</span> self.value == other.value

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__lt__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(other, ConsistentNumber):
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>
        <span class="hljs-keyword">return</span> self.value &lt; other.value
</code></pre>
<h3 id="heading-2-return-notimplemented"><strong>2. Return NotImplemented</strong></h3>
<p>When an operation doesn't make sense, return <code>NotImplemented</code> to let Python try the reverse operation:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Money</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__add__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(other, Money):
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>
        <span class="hljs-comment"># ... rest of the implementation</span>
</code></pre>
<h3 id="heading-3-keep-it-simple"><strong>3. Keep It Simple</strong></h3>
<p>Magic methods should be simple and predictable. Avoid complex logic that could lead to unexpected behavior:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Good: Simple and predictable</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SimpleContainer</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>
        self.items = []

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getitem__</span>(<span class="hljs-params">self, index</span>):</span>
        <span class="hljs-keyword">return</span> self.items[index]

<span class="hljs-comment"># Bad: Complex and potentially confusing</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ComplexContainer</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>
        self.items = []
        self.access_count = <span class="hljs-number">0</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getitem__</span>(<span class="hljs-params">self, index</span>):</span>
        self.access_count += <span class="hljs-number">1</span>
        <span class="hljs-keyword">if</span> self.access_count &gt; <span class="hljs-number">100</span>:
            <span class="hljs-keyword">raise</span> RuntimeError(<span class="hljs-string">"Too many accesses"</span>)
        <span class="hljs-keyword">return</span> self.items[index]
</code></pre>
<h3 id="heading-4-document-behavior"><strong>4. Document Behavior</strong></h3>
<p>Clearly document how your magic methods behave, especially if they deviate from standard expectations:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CustomDict</span>(<span class="hljs-params">dict</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__missing__</span>(<span class="hljs-params">self, key</span>):</span>
        <span class="hljs-string">"""
        Called when a key is not found in the dictionary.
        Creates a new list for the key and returns it.
        This allows for automatic list creation when accessing
        non-existent keys.
        """</span>
        self[key] = []
        <span class="hljs-keyword">return</span> self[key]
</code></pre>
<h3 id="heading-5-consider-performance"><strong>5. Consider Performance</strong></h3>
<p>Be aware of the performance implications, especially for frequently called methods:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OptimizedContainer</span>:</span>
    __slots__ = [<span class="hljs-string">'items'</span>]  <span class="hljs-comment"># Use __slots__ for better performance</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self.items = []

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getitem__</span>(<span class="hljs-params">self, index</span>):</span>
        <span class="hljs-keyword">return</span> self.items[index]  <span class="hljs-comment"># Direct access is faster</span>
</code></pre>
<h3 id="heading-6-handle-edge-cases"><strong>6. Handle Edge Cases</strong></h3>
<p>Always consider edge cases and handle them appropriately:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SafeContainer</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getitem__</span>(<span class="hljs-params">self, key</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(key, (int, slice)):
            <span class="hljs-keyword">raise</span> TypeError(<span class="hljs-string">"Index must be integer or slice"</span>)
        <span class="hljs-keyword">if</span> key &lt; <span class="hljs-number">0</span>:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"Index cannot be negative"</span>)
        <span class="hljs-comment"># ... rest of the implementation</span>
</code></pre>
<h2 id="heading-wrapping-up"><strong>Wrapping Up</strong></h2>
<p>Python's magic methods provide a powerful way to make your classes behave like built-in types, enabling more intuitive and expressive code. Throughout this guide, we've explored how these methods work and how to use them effectively.</p>
<h3 id="heading-key-takeaways"><strong>Key Takeaways</strong></h3>
<ol>
<li><p><strong>Object representation</strong>:</p>
<ul>
<li><p>Use <code>__str__</code> for user-friendly output</p>
</li>
<li><p>Use <code>__repr__</code> for debugging and development</p>
</li>
</ul>
</li>
<li><p><strong>Operator overloading</strong>:</p>
<ul>
<li><p>Implement arithmetic and comparison operators</p>
</li>
<li><p>Return <code>NotImplemented</code> for unsupported operations</p>
</li>
<li><p>Use <code>@total_ordering</code> for consistent comparisons</p>
</li>
</ul>
</li>
<li><p><strong>Container behavior</strong>:</p>
<ul>
<li><p>Implement sequence and mapping protocols</p>
</li>
<li><p>Consider performance for frequently used operations</p>
</li>
<li><p>Handle edge cases appropriately</p>
</li>
</ul>
</li>
<li><p><strong>Resource management</strong>:</p>
<ul>
<li><p>Use context managers for proper resource handling</p>
</li>
<li><p>Implement <code>__enter__</code> and <code>__exit__</code> for cleanup</p>
</li>
<li><p>Handle exceptions in <code>__exit__</code></p>
</li>
</ul>
</li>
<li><p><strong>Performance optimization</strong>:</p>
<ul>
<li><p>Use <code>__slots__</code> for memory efficiency</p>
</li>
<li><p>Cache computed values when appropriate</p>
</li>
<li><p>Minimize method calls in frequently used code</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-when-to-use-magic-methods"><strong>When to Use Magic Methods</strong></h3>
<p>Magic methods are most useful when you need to:</p>
<ol>
<li><p>Create custom data structures</p>
</li>
<li><p>Implement domain-specific types</p>
</li>
<li><p>Manage resources properly</p>
</li>
<li><p>Add special behavior to your classes</p>
</li>
<li><p>Make your code more Pythonic</p>
</li>
</ol>
<h3 id="heading-when-to-avoid-magic-methods"><strong>When to Avoid Magic Methods</strong></h3>
<p>Avoid magic methods when:</p>
<ol>
<li><p>Simple attribute access is sufficient</p>
</li>
<li><p>The behavior would be confusing or unexpected</p>
</li>
<li><p>Performance is critical and magic methods would add overhead</p>
</li>
<li><p>The implementation would be overly complex</p>
</li>
</ol>
<p>Remember that with great power comes great responsibility. Use magic methods judiciously, keeping in mind their performance implications and the principle of least surprise. When used appropriately, magic methods can significantly enhance the readability and expressiveness of your code.</p>
<h2 id="heading-references-and-further-reading"><strong>References and Further Reading</strong></h2>
<h3 id="heading-official-python-documentation"><strong>Official Python Documentation</strong></h3>
<ol>
<li><p><a target="_blank" href="https://docs.python.org/3/reference/datamodel.html">Python Data Model - Official Documentation</a> - Comprehensive guide to Python's data model and magic methods.</p>
</li>
<li><p><a target="_blank" href="https://docs.python.org/3/library/functools.html#functools.total_ordering">functools.total_ordering</a> - Documentation for the total_ordering decorator that automatically fills in missing comparison methods.</p>
</li>
<li><p><a target="_blank" href="https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers">Python Special Method Names</a> - Official reference for special method identifiers in Python.</p>
</li>
<li><p><a target="_blank" href="https://docs.python.org/3/library/collections.abc.html">Collections Abstract Base Classes</a> - Learn about abstract base classes for containers which define the interfaces that your container classes can implement.</p>
</li>
</ol>
<h3 id="heading-community-resources"><strong>Community Resources</strong></h3>
<ol start="5">
<li><a target="_blank" href="https://rszalski.github.io/magicmethods/">A Guide to Python's Magic Methods - Rafe Kettler</a> - Practical examples of magic methods and common use cases.</li>
</ol>
<h3 id="heading-further-reading"><strong>Further Reading</strong></h3>
<p>If you enjoyed this article, you might find these Python-related articles on my <a target="_blank" href="https://wewake.dev">personal blog</a> useful:</p>
<ol>
<li><p><a target="_blank" href="https://wewake.dev/posts/practical-experiments-for-django-orm-query-optimizations/">Practical Experiments for Django ORM Query Optimizations</a> - Learn how to optimize your Django ORM queries with practical examples and experiments.</p>
</li>
<li><p><a target="_blank" href="https://wewake.dev/posts/high-cost-of-sync-uwsgi/">The High Cost of Synchronous uWSGI</a> - Understand the performance implications of synchronous processing in uWSGI and how it affects your Python web applications.</p>
</li>
</ol>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Social Learning Platform using Next.js, Stream, and Supabase ]]>
                </title>
                <description>
                    <![CDATA[ Social media and real-time communication have transformed how people interact, making it easier to share ideas, collaborate, and learn from others, regardless of location. From professional networks to online study groups, these platforms allow vario... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-social-learning-platform-using-nextjs-stream-and-supabase/</link>
                <guid isPermaLink="false">67c5c657e04393229206983b</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ David Asaolu ]]>
                </dc:creator>
                <pubDate>Mon, 03 Mar 2025 15:10:15 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741009946459/dba65929-1b65-4278-9601-4d047042753a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Social media and real-time communication have transformed how people interact, making it easier to share ideas, collaborate, and learn from others, regardless of location. From professional networks to online study groups, these platforms allow various forms of communication such as instant messaging, video calls, and content sharing.</p>
<p>In this tutorial, you'll learn how to build a social learning platform that connects students with professionals across various fields. The platform enables users to:</p>
<ul>
<li><p>Schedule video conferencing sessions that students can join,</p>
</li>
<li><p>Share posts or announcements about trending tools and upcoming sessions, and</p>
</li>
<li><p>Create community channels where students can engage with one another.</p>
</li>
</ul>
<p>The <a target="_blank" href="https://getstream.io/video/sdk/">Stream Video &amp; Audio SDK</a> and <a target="_blank" href="https://getstream.io/chat/sdk/">Stream Chat SDK</a> will enable us to integrate video calls and community channels easily into the application.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740662075821/1a004f19-4889-4b57-921b-062cf1927261.gif" alt="Cartoon person with pink hair and green glasses holding a smartphone, which displays heart, poop, and smiley face emojis on the screen." class="image--center mx-auto" width="500" height="331" loading="lazy"></p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-app-overview">App Overview</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-set-up-server-side-authentication-with-supabase">How to Set up Server-Side Authentication with Supabase</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-to-configure-supabase-authentication-in-a-nextjs-application">How to Configure Supabase Authentication in a Next.js application</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-student-authentication-with-supabase">Student Authentication with Supabase</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-instructor-authentication-with-supabase">Instructor Authentication with Supabase</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-the-application-database-design">The Application Database Design</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-access-policy-for-the-announcements-table">Access Policy for the Announcements Table</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-access-policy-for-the-instructors-table">Access Policy for the Instructors Table</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-access-policy-for-the-students-table">Access Policy for the Students Table</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-add-a-video-conferencing-feature-with-stream">How to Add a Video Conferencing Feature with Stream</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-setting-up-stream-video-amp-audio-sdk-in-nextjs">Setting Up Stream Video &amp; Audio SDK in Next.js</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-creating-and-scheduling-calls-with-stream">Creating and Scheduling Calls with Stream</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-joining-stream-video-calls">Joining Stream Video Calls</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-stream-call-ui-components">Stream Call UI Components</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-integrate-a-group-chat-feature-using-stream-chat-messaging">How to Integrate a Group Chat Feature Using Stream Chat Messaging</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-setting-up-the-stream-chat-sdk-in-nextjs">Setting Up the Stream Chat SDK in Next.js</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-stream-chat-ui-components">Stream Chat UI Components</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-next-steps">Next Steps</a></p>
</li>
</ul>
<h2 id="heading-app-overview">App Overview</h2>
<p>The application consists of two types of users (students and instructors), each with access to specific features:</p>
<p>Students can do the following:</p>
<ul>
<li><p>View an activity feed with posts from instructors and react to them.</p>
</li>
<li><p>Follow instructors in their field of interest.</p>
</li>
<li><p>Join upcoming video sessions and community channels.</p>
</li>
<li><p>Each student has an interest attribute that helps match them with relevant instructors.</p>
</li>
</ul>
<p>Instructors can also:</p>
<ul>
<li><p>Access a dashboard showing their follower count and post activity.</p>
</li>
<li><p>Schedule video conferences for students to join.</p>
</li>
<li><p>Make announcements or share posts.</p>
</li>
<li><p>Create community channels (if they haven't already).</p>
</li>
<li><p>The platform suggests instructors to students based on shared career interests.</p>
</li>
</ul>
<p>Here is an image showing the various functions that the users can perform:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740723106527/fd8af07a-1919-42f0-9e9a-ed380a0b77cc.png" alt="Flowchart titled &quot;User Functions&quot; showing a hierarchy. &quot;Users&quot; split into &quot;Students&quot; and &quot;Instructors&quot;. Students can &quot;Follow Instructors&quot;, &quot;Join Video Sessions&quot;, &quot;Join Community Channels&quot;, and &quot;Read and React to Posts&quot;. Instructors can &quot;View Dashboard&quot;, &quot;Create Posts&quot;, &quot;Schedule Video Sessions&quot;, and &quot;Create Community Channels&quot;." class="image--center mx-auto" width="1600" height="840" loading="lazy"></p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To fully understand this tutorial, you need to have a basic understanding of React or Next.js.</p>
<p>We will use the following tools:</p>
<ul>
<li><p><a target="_blank" href="https://supabase.com/docs">Supabase</a>: a Backend-as-a-service platform that makes it easy to integrate authentication, database, real-time communication, file storage, and edge functions within your software applications. It also supports multiple programming languages.</p>
</li>
<li><p><a target="_blank" href="https://getstream.io/chat/docs/sdk/react/">Stream Chat</a> and <a target="_blank" href="https://getstream.io/video/docs/react/">Audio &amp; Video SDK</a>: a real-time communication platform that enables you to add video, chat, and various types of communication to your application.</p>
</li>
<li><p><a target="_blank" href="https://ui.shadcn.com/docs/installation/next">Shadcn UI</a>: a UI component library that provides customizable, beautifully designed, and accessible UI components for your applications.</p>
</li>
</ul>
<p>Create a Next.js project by running the following code snippet:</p>
<pre><code class="lang-bash">npx create-next-app stream-lms
</code></pre>
<p>Install the package dependencies for the project:</p>
<pre><code class="lang-bash">npm install @supabase/supabase-js @supabase/ssr @stream-io/node-sdk @stream-io/video-react-sdk stream-chat stream-chat-react @emoji-mart/data @emoji-mart/react
</code></pre>
<p>To install the Shadcn UI library, follow <a target="_blank" href="https://ui.shadcn.com/docs/installation/next">the installation guide.</a></p>
<p>Once everything is set up, your Next.js project is ready. Now, let's start building! 🚀</p>
<h2 id="heading-how-to-set-up-server-side-authentication-with-supabase">How to Set up Server-Side Authentication with Supabase</h2>
<p>Here, you'll learn how to configure Supabase, add server-side authentication, and protect pages from unauthorized users in a Next.js application. You'll also learn how to handle the authentication logic efficiently using <a target="_blank" href="https://nextjs.org/docs/13/app/api-reference/functions/server-actions#with-client-components">Next.js server actions</a>.</p>
<h3 id="heading-how-to-configure-supabase-authentication-in-a-nextjs-application">How to Configure Supabase Authentication in a Next.js application</h3>
<p>First, create a <a target="_blank" href="https://supabase.com/">Supabase account</a> and an organization that will contain your various Supabase projects.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740653729412/35339612-4688-489e-b9d2-2cc825962519.png" alt="Screenshot of a form on the Supabase website to create a new organization. It includes fields for organization name, type, and plan, with options such as &quot;Personal&quot; and &quot;Free - $0/month.&quot; There are buttons for &quot;Cancel&quot; and &quot;Create organization.&quot;" class="image--center mx-auto" width="2353" height="1082" loading="lazy"></p>
<p>Add a new Supabase project to the organisation and copy the following credentials on your dashboard into a <code>.env.local</code> file at the root of your project:</p>
<pre><code class="lang-bash">NEXT_PUBLIC_SUPABASE_ANON_KEY=&lt;anon_key_from_Supabase_dashboard&gt;
NEXT_PUBLIC_SUPABASE_URL=&lt;supabase_project_url&gt;
</code></pre>
<p>Create a <code>utils/supabase</code> folder at the root of the Next.js project and add the following files to the folder: <code>client.ts</code>, <code>middleware.ts</code>, and <code>server.ts</code>.</p>
<pre><code class="lang-bash">mkdir utils &amp;&amp; <span class="hljs-built_in">cd</span> utils
mkdir supabase &amp;&amp; <span class="hljs-built_in">cd</span> supabase
touch client.ts middleware.ts server.ts
</code></pre>
<p>Copy the following code into <code>utils/supabase/client.ts</code>. This initializes a Supabase browser client to interact with Supabase on client-side routes:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { createBrowserClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@supabase/ssr"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createClient</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> createBrowserClient(
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
    );
}
</code></pre>
<p>Next, copy the following code into <code>utils/supabase/server.ts</code>. This creates a Supabase server client for handling authentication and interacting with Supabase in server-side requests:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { createServerClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@supabase/ssr"</span>;
<span class="hljs-keyword">import</span> { cookies } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/headers"</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">createClient</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> cookieStore = <span class="hljs-keyword">await</span> cookies();

    <span class="hljs-keyword">return</span> createServerClient(
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
        {
            cookies: {
                getAll() {
                    <span class="hljs-keyword">return</span> cookieStore.getAll();
                },
                setAll(cookiesToSet) {
                    <span class="hljs-keyword">try</span> {
                        cookiesToSet.forEach(<span class="hljs-function">(<span class="hljs-params">{ name, value, options }</span>) =&gt;</span>
                            cookieStore.set(name, value, options)
                        );
                    } <span class="hljs-keyword">catch</span> {
                        <span class="hljs-comment">// The `setAll` method was called from a Server Component.</span>
                        <span class="hljs-comment">// This can be ignored if you have middleware refreshing</span>
                        <span class="hljs-comment">// user sessions.</span>
                    }
                },
            },
        }
    );
}
</code></pre>
<p>Now, copy the following code into <code>utils/supabase/middleware.ts</code>. This middleware creates authentication cookies and protects pages from unauthorized access:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { createServerClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@supabase/ssr"</span>;
<span class="hljs-keyword">import</span> { NextResponse, <span class="hljs-keyword">type</span> NextRequest } <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">updateSession</span>(<span class="hljs-params">request: NextRequest</span>) </span>{
    <span class="hljs-keyword">let</span> supabaseResponse = NextResponse.next({
        request,
    });
    <span class="hljs-comment">//👇🏻 creates the Supabase cookie functions</span>
    <span class="hljs-keyword">const</span> supabase = createServerClient(
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
        {
            cookies: {
                getAll() {
                    <span class="hljs-keyword">return</span> request.cookies.getAll();
                },
                setAll(cookiesToSet) {
                    cookiesToSet.forEach(<span class="hljs-function">(<span class="hljs-params">{ name, value }</span>) =&gt;</span>
                        request.cookies.set(name, value)
                    );
                    supabaseResponse = NextResponse.next({
                        request,
                    });
                    cookiesToSet.forEach(<span class="hljs-function">(<span class="hljs-params">{ name, value, options }</span>) =&gt;</span>
                        supabaseResponse.cookies.set(name, value, options)
                    );
                },
            },
        }
    );

    <span class="hljs-comment">// 👉🏻 placeholder for protected route controller</span>
}
</code></pre>
<p>To enforce authentication, add the following code inside the placeholder in <code>middleware.ts</code><strong>.</strong> This checks if a user is signed in and redirects unauthenticated users to the login page:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">//👇🏻 gets current user</span>
<span class="hljs-keyword">const</span> {
    data: { user },
} = <span class="hljs-keyword">await</span> supabase.auth.getUser();

<span class="hljs-comment">//👇🏻 declares protected routes</span>
<span class="hljs-keyword">if</span> (
    !user &amp;&amp;
    request.nextUrl.pathname !== <span class="hljs-string">"/"</span> &amp;&amp;
    !request.nextUrl.pathname.startsWith(<span class="hljs-string">"/instructor/auth"</span>) &amp;&amp;
    !request.nextUrl.pathname.startsWith(<span class="hljs-string">"/student/auth"</span>)
) {
    <span class="hljs-comment">//👇🏻 Redirect unauthenticated users to the login page</span>
    <span class="hljs-keyword">const</span> url = request.nextUrl.clone();
    url.pathname = <span class="hljs-string">"/student/auth/login"</span>; <span class="hljs-comment">// 👈🏼 redirect page</span>
    <span class="hljs-keyword">return</span> NextResponse.redirect(url);
}
<span class="hljs-comment">//👇🏻 returns Supabase response</span>
<span class="hljs-keyword">return</span> supabaseResponse;
</code></pre>
<p>Add another <code>middleware.ts</code> file to the root of the Next.js project and copy the following code into the file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { <span class="hljs-keyword">type</span> NextRequest } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/server"</span>;
<span class="hljs-keyword">import</span> { updateSession } <span class="hljs-keyword">from</span> <span class="hljs-string">"./utils/supabase/middleware"</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">middleware</span>(<span class="hljs-params">request: NextRequest</span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> updateSession(request);
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> config = {
    matcher: [
        <span class="hljs-comment">/*
         * Match all request paths except for the ones starting with:
         * - _next/static (static files)
         * - _next/image (image optimization files)
         * - favicon.ico (favicon file)
         * Feel free to modify this pattern to include more paths.
         */</span>
        <span class="hljs-string">"/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)"</span>,
    ],
};
</code></pre>
<p>Finally, create an <a target="_blank" href="https://github.com/dha-stix/stream-lms/blob/main/src/app/auth/confirm/route.ts"><strong>auth/confirm</strong></a> route and <a target="_blank" href="https://github.com/dha-stix/stream-lms/blob/main/src/app/error/page.tsx"><strong>error</strong></a> page within the Next.js app folder.</p>
<p>You've successfully <a target="_blank" href="https://supabase.com/docs/guides/auth/server-side/nextjs">configured authentication in your Next.js project</a> using Supabase.</p>
<h3 id="heading-student-authentication-with-supabase">Student Authentication with Supabase</h3>
<p>In this section, you will learn how to create the signup and login functions for the students within the application.</p>
<p>First, create an <strong>actions</strong> folder in the root of your Next.js project and add an <code>auth.ts</code> file inside it. This file will contain all Supabase authentication functions.</p>
<p>Add the following imports to the top of the <code>auth.ts</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use server"</span>;
<span class="hljs-keyword">import</span> { revalidatePath } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/cache"</span>;
<span class="hljs-keyword">import</span> { redirect } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/navigation"</span>;
<span class="hljs-keyword">import</span> { createClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils/supabase/server"</span>;
</code></pre>
<p>Next, you need to create the server functions that accept form data from the client and sign users up or log them in as students.</p>
<p>Copy the following code snippet into the <code>actions/auth.ts</code> file to create the user sign-up function:</p>
<pre><code class="lang-typescript"><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">studentSignUp</span>(<span class="hljs-params">formData: FormData</span>) </span>{
    <span class="hljs-keyword">const</span> supabase = <span class="hljs-keyword">await</span> createClient();

    <span class="hljs-comment">//👇🏻 Extract form data</span>
    <span class="hljs-keyword">const</span> credentials = {
        email: formData.get(<span class="hljs-string">"email"</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
        password: formData.get(<span class="hljs-string">"password"</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
        interest: formData.get(<span class="hljs-string">"interest"</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
        name: formData.get(<span class="hljs-string">"name"</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
    };

    <span class="hljs-comment">//👇🏻 Supabase sign up function (options attribute :- for user metadata)</span>
    <span class="hljs-keyword">const</span> { data, error } = <span class="hljs-keyword">await</span> supabase.auth.signUp({
        email: credentials.email,
        password: credentials.password,
        options: {
            data: {
                interest: credentials.interest,
                name: credentials.name,
            },
        },
    });

    <span class="hljs-comment">//👉🏻 return user or error object</span>
}
</code></pre>
<p>The code snippet above accepts the form credentials such as email, password, interest, and name, and signs the user up as a Supabase user.</p>
<p>Modify the function to return the user or error object.</p>
<pre><code class="lang-typescript"><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">studentSignUp</span>(<span class="hljs-params">formData: FormData</span>) </span>{
    <span class="hljs-comment">//...form inputs and supabase functions</span>

    <span class="hljs-keyword">if</span> (error) {
        <span class="hljs-keyword">return</span> { error: error.message, status: error.status, user: <span class="hljs-literal">null</span> };
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (data.user?.identities?.length === <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">return</span> { error: <span class="hljs-string">"User already exists"</span>, status: <span class="hljs-number">409</span>, user: <span class="hljs-literal">null</span> };
    }

    revalidatePath(<span class="hljs-string">"/"</span>, <span class="hljs-string">"layout"</span>);
    <span class="hljs-keyword">return</span> { error: <span class="hljs-literal">null</span>, status: <span class="hljs-number">200</span>, user: data.user };
}
</code></pre>
<p>Create the student login function as shown below:</p>
<pre><code class="lang-typescript"><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">studentLogIn</span>(<span class="hljs-params">formData: FormData</span>) </span>{
    <span class="hljs-keyword">const</span> supabase = <span class="hljs-keyword">await</span> createClient();

    <span class="hljs-keyword">const</span> credentials = {
        email: formData.get(<span class="hljs-string">"email"</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
        password: formData.get(<span class="hljs-string">"password"</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
    };
    <span class="hljs-keyword">const</span> { data, error } = <span class="hljs-keyword">await</span> supabase.auth.signInWithPassword(credentials);

    <span class="hljs-keyword">if</span> (error) {
        <span class="hljs-keyword">return</span> { error: error.message, status: error.status, user: <span class="hljs-literal">null</span> };
    }
    <span class="hljs-comment">//👇🏻 only instructors have an image attribute</span>
    <span class="hljs-keyword">if</span> (data &amp;&amp; data.user.user_metadata.image) {
        <span class="hljs-keyword">return</span> { error: <span class="hljs-string">"You are not a student"</span>, status: <span class="hljs-number">400</span>, user: <span class="hljs-literal">null</span> };
    }

    <span class="hljs-comment">//👉🏻 create a student row and add to the database</span>

    revalidatePath(<span class="hljs-string">"/"</span>, <span class="hljs-string">"layout"</span>);
    <span class="hljs-keyword">return</span> { error: <span class="hljs-literal">null</span>, status: <span class="hljs-number">200</span>, user: data.user };
}
</code></pre>
<p>The code above takes the student's email and password to log them into the application.</p>
<ul>
<li><p>If an error occurs, it returns an error message.</p>
</li>
<li><p>If the user object includes an image attribute (indicating that they are an instructor), they are prevented from logging in.</p>
</li>
</ul>
<p>Once the student is signed in, you must store their details in a Supabase table. This allows you to add a <code>following_list</code> column that tracks the instructors they follow. The list will be updated whenever the student follows or unfollows an instructor.</p>
<pre><code class="lang-typescript"><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">studentLogIn</span>(<span class="hljs-params">formData: FormData</span>) </span>{
    <span class="hljs-comment">//...other functions</span>

    <span class="hljs-keyword">const</span> { data: existingUser } = <span class="hljs-keyword">await</span> supabase
        .from(<span class="hljs-string">"students"</span>)
        .select()
        .eq(<span class="hljs-string">"email"</span>, credentials.email)
        .single();

    <span class="hljs-comment">//👇🏻 if student doesn't exist</span>
    <span class="hljs-keyword">if</span> (!existingUser) {
        <span class="hljs-keyword">const</span> { error: insertError } = <span class="hljs-keyword">await</span> supabase.from(<span class="hljs-string">"students"</span>).insert({
            email: credentials.email,
            name: data.user.user_metadata.name,
            interest: data.user.user_metadata.interest,
            id: data.user.id,
            following_list: [] <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>[],
        });

        <span class="hljs-keyword">if</span> (insertError) {
            <span class="hljs-keyword">return</span> { error: insertError.message, status: <span class="hljs-number">500</span>, user: <span class="hljs-literal">null</span> };
        }
    }

    revalidatePath(<span class="hljs-string">"/"</span>, <span class="hljs-string">"layout"</span>);
    <span class="hljs-keyword">return</span> { error: <span class="hljs-literal">null</span>, status: <span class="hljs-number">200</span>, user: data.user };
}
</code></pre>
<p>Every time a student logs in, the code checks if they already exist in the <code>students</code> table.</p>
<ul>
<li><p>If the student is found, no new entry is created.</p>
</li>
<li><p>If the student is not found, a new row with their details is added.</p>
</li>
</ul>
<p>Each student’s data includes two primary keys: <code>id</code> and <code>email</code> and additional columns: <code>interest</code>, <code>name</code>, and <code>following_list</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740654932982/724fcf11-55e3-4163-9a6e-0b2771d0874c.gif" alt="Student registration form with fields for full name, email address, interest, and password. Includes a &quot;Register&quot; button and a sign-in option for existing accounts. The browser tab shows the URL &quot;localhost:3000&quot;." class="image--center mx-auto" width="800" height="473" loading="lazy"></p>
<h3 id="heading-instructor-authentication-with-supabase">Instructor Authentication with Supabase</h3>
<p>The instructor's user object is quite different from the student's. It includes data such as email, password, name, interest, occupation, bio, URL, and image.</p>
<p>Add the following function to <code>actions/auth.ts</code> to handle instructor sign-ups:</p>
<pre><code class="lang-typescript"><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">instructorSignUp</span>(<span class="hljs-params">formData: FormData</span>) </span>{
    <span class="hljs-keyword">const</span> supabase = <span class="hljs-keyword">await</span> createClient();

    <span class="hljs-comment">//👇🏻 get user credentials from the form</span>
    <span class="hljs-keyword">const</span> credentials = {
        email: formData.get(<span class="hljs-string">"email"</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
        password: formData.get(<span class="hljs-string">"password"</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
        interest: formData.get(<span class="hljs-string">"interest"</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
        name: formData.get(<span class="hljs-string">"name"</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
        occupation: formData.get(<span class="hljs-string">"occupation"</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
        bio: formData.get(<span class="hljs-string">"bio"</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
        url: formData.get(<span class="hljs-string">"url"</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>,
        image: formData.get(<span class="hljs-string">"image"</span>) <span class="hljs-keyword">as</span> File,
    };

    <span class="hljs-comment">//👉🏻 following code snippet below</span>
}
</code></pre>
<p>Next, upload the image to Supabase Storage and retrieve its download URL before signing up the user as an instructor. Update the <code>instructorSignUp</code> function to show this:</p>
<pre><code class="lang-typescript"><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">instructorSignUp</span>(<span class="hljs-params">formData: FormData</span>) </span>{
    <span class="hljs-comment">//👇🏻 upload instructor's image</span>
    <span class="hljs-keyword">const</span> { data: imageData, error: imageError } = <span class="hljs-keyword">await</span> supabase.storage
        .from(<span class="hljs-string">"headshots"</span>)
        .upload(<span class="hljs-string">`<span class="hljs-subst">${crypto.randomUUID()}</span>/image`</span>, credentials.image);

    <span class="hljs-keyword">if</span> (imageError) {
        <span class="hljs-keyword">return</span> { error: imageError.message, status: <span class="hljs-number">500</span>, user: <span class="hljs-literal">null</span> };
    }
    <span class="hljs-comment">//👇🏻 get the image URL</span>
    <span class="hljs-keyword">const</span> imageURL = <span class="hljs-string">`<span class="hljs-subst">${process.env.STORAGE_URL!}</span><span class="hljs-subst">${imageData.fullPath}</span>`</span>;

    <span class="hljs-comment">//👇🏻 authenticate user as instructor</span>
    <span class="hljs-keyword">const</span> { data, error } = <span class="hljs-keyword">await</span> supabase.auth.signUp({
        email: credentials.email,
        password: credentials.password,
        options: {
            data: {
                interest: credentials.interest,
                name: credentials.name,
                occupation: credentials.occupation,
                bio: credentials.bio,
                url: credentials.url,
                image: imageURL,
            },
        },
    });

    <span class="hljs-comment">//👇🏻 return user or error object</span>
    <span class="hljs-keyword">if</span> (error) {
        <span class="hljs-keyword">return</span> { error: error.message, status: error.status, user: <span class="hljs-literal">null</span> };
    }

    revalidatePath(<span class="hljs-string">"/"</span>, <span class="hljs-string">"layout"</span>);
    <span class="hljs-keyword">return</span> { error: <span class="hljs-literal">null</span>, status: <span class="hljs-number">200</span>, user: data.user };
}
</code></pre>
<p>Finally, an <a target="_blank" href="https://github.com/dha-stix/stream-lms/blob/main/actions/auth.ts">instructor login function</a> that authenticates the user, similar to the student login function, should be created. It should check whether the instructor already exists in the <code>instructors</code> table. If the instructor does not exist, execute the function to add the instructor's user object to the database table.</p>
<p>Here is the Supabase function for adding an instructor to the table:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> { error: insertError } = <span class="hljs-keyword">await</span> supabase.from(<span class="hljs-string">"instructors"</span>).insert({
    email: credentials.email,
    name: data.user.user_metadata.name,
    occupation: data.user.user_metadata.occupation,
    bio: data.user.user_metadata.bio,
    url: data.user.user_metadata.url,
    image: data.user.user_metadata.image,
    id: data.user.id,
    interest: data.user.user_metadata.interest,
    followers: [],
});
</code></pre>
<p>The <code>instructors</code> table includes an additional <code>followers</code> attribute, which stores an array of student IDs following the instructor. You can find the <a target="_blank" href="https://github.com/dha-stix/stream-lms/blob/main/actions/auth.ts">complete code on GitHub</a>.</p>
<p>Additionally, authentication functions like <a target="_blank" href="https://github.com/dha-stix/stream-lms/blob/main/actions/auth.ts"><strong>getUserSession</strong></a> and <a target="_blank" href="https://github.com/dha-stix/stream-lms/blob/main/actions/auth.ts"><strong>logOut</strong></a> must be created. These functions will retrieve the current user's object and allow them to log out when necessary, such as when clicking a logout button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740655472825/4c038412-fe6d-4449-b2a9-d9d3649da2f8.gif" alt="Instructor login page with fields for email and password, a &quot;Sign in&quot; button, and a link to create an account. A sidebar on the left displays &quot;LinkedUp&quot; with a &quot;Student Sign-in&quot; link." class="image--center mx-auto" width="800" height="473" loading="lazy"></p>
<h2 id="heading-the-application-database-design">The Application Database Design</h2>
<p>In the previous section, we created two database tables: <code>instructors</code> and <code>students</code>, which store instructors and students separately. Instructors can also upload headshot images to <a target="_blank" href="https://supabase.com/docs/guides/storage/quickstart">Supabase Storage</a>.</p>
<p>In this section, you'll learn how to create these tables, define their access policies, and retrieve or modify data within the tables.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Announcements (data type)</strong></td><td><strong>Instructors (data type)</strong></td><td><strong>Students (data type)</strong></td></tr>
</thead>
<tbody>
<tr>
<td>id (int8)</td><td>id (uuid)</td><td>id (uuid)</td></tr>
<tr>
<td>created_at (timestamptz)</td><td>created_at (timestamptz)</td><td>created_at (timestamptz)</td></tr>
<tr>
<td>author_name (text)</td><td>name (text)</td><td>email (text)</td></tr>
<tr>
<td>interest (text)</td><td>email (text)</td><td>name (text)</td></tr>
<tr>
<td>author_title (text)</td><td>occupation (text)</td><td>interest (text)</td></tr>
<tr>
<td>author_id (uuid)</td><td>bio (text)</td><td>following_list (uuid[])</td></tr>
<tr>
<td>content (text)</td><td>url (text)</td><td></td></tr>
<tr>
<td>likes (uuid [])</td><td>interest (text)</td><td></td></tr>
<tr>
<td>author_image (text)</td><td>image (text)</td><td></td></tr>
<tr>
<td></td><td>followers (uuid[])</td></tr>
</tbody>
</table>
</div><p><strong>Note:</strong> The <code>instructors</code> table includes an <code>image</code> column that stores the instructor's headshot URL. You can obtain this by creating a Supabase bucket named <code>headshot</code> and uploading the image when the instructor signs up.</p>
<p>The <code>instructors</code> and <code>students</code> tables have two primary keys: <code>id</code> and <code>email</code>.</p>
<p>Supabase allows you to define policies for your tables, controlling the operations different users can perform within the application.</p>
<p>Next, let’s create the access policies for each table.</p>
<h3 id="heading-access-policy-for-the-announcements-table">Access Policy for the Announcements Table</h3>
<p>The <code>announcements</code> table has four access policies:</p>
<ul>
<li><p>Enable delete operation for users based on their user ID.</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">alter</span> <span class="hljs-keyword">policy</span> <span class="hljs-string">"Enable delete for users based on user_id"</span>
  <span class="hljs-keyword">on</span> <span class="hljs-string">"public"</span>.<span class="hljs-string">"announcements"</span>
  <span class="hljs-keyword">to</span> <span class="hljs-keyword">public</span>
  <span class="hljs-keyword">using</span> (
    (( <span class="hljs-keyword">SELECT</span> auth.uid() <span class="hljs-keyword">AS</span> uid) = author_id)
  );
</code></pre>
</li>
<li><p>Enable insert operation for authenticated users only.</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">alter</span> <span class="hljs-keyword">policy</span> <span class="hljs-string">"Enable insert for authenticated users only"</span>
  <span class="hljs-keyword">on</span> <span class="hljs-string">"public"</span>.<span class="hljs-string">"announcements"</span>
  <span class="hljs-keyword">to</span> <span class="hljs-keyword">authenticated</span>
  <span class="hljs-keyword">with</span> <span class="hljs-keyword">check</span> (
    <span class="hljs-literal">true</span>
  );
</code></pre>
</li>
<li><p>Enable read access for all users.</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">alter</span> <span class="hljs-keyword">policy</span> <span class="hljs-string">"Enable read access for all users"</span>
  <span class="hljs-keyword">on</span> <span class="hljs-string">"public"</span>.<span class="hljs-string">"announcements"</span>
  <span class="hljs-keyword">to</span> <span class="hljs-keyword">public</span>
  <span class="hljs-keyword">using</span> (
    <span class="hljs-literal">true</span>
  );
</code></pre>
</li>
<li><p>Enable update operation for authenticated users only.</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">alter</span> <span class="hljs-keyword">policy</span> <span class="hljs-string">"Enable update for authenticated users"</span>
  <span class="hljs-keyword">on</span> <span class="hljs-string">"public"</span>.<span class="hljs-string">"announcements"</span>
  <span class="hljs-keyword">to</span> <span class="hljs-keyword">authenticated</span>
  <span class="hljs-keyword">using</span> (
    (auth.role() = <span class="hljs-string">'authenticated'</span>::<span class="hljs-built_in">text</span>)
  );
</code></pre>
</li>
</ul>
<h3 id="heading-access-policy-for-the-instructors-table">Access Policy for the Instructors Table</h3>
<p>The <code>instructors</code> table has three policies:</p>
<ul>
<li><p>Allow only authenticated users to update the <code>instructors</code> table.</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">alter</span> <span class="hljs-keyword">policy</span> <span class="hljs-string">"Allow only authenticated users"</span>
  <span class="hljs-keyword">on</span> <span class="hljs-string">"public"</span>.<span class="hljs-string">"instructors"</span>
  <span class="hljs-keyword">to</span> <span class="hljs-keyword">authenticated</span>
  <span class="hljs-keyword">using</span> (
    (auth.role() = <span class="hljs-string">'authenticated'</span>::<span class="hljs-built_in">text</span>)
  );
</code></pre>
</li>
<li><p>Enable insert operation for authenticated users only.</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">alter</span> <span class="hljs-keyword">policy</span> <span class="hljs-string">"Enable insert for authenticated users only"</span>
  <span class="hljs-keyword">on</span> <span class="hljs-string">"public"</span>.<span class="hljs-string">"instructors"</span>
  <span class="hljs-keyword">to</span> <span class="hljs-keyword">authenticated</span>
  <span class="hljs-keyword">with</span> <span class="hljs-keyword">check</span> (
    <span class="hljs-literal">true</span>
  );
</code></pre>
</li>
<li><p>Enable read access for all users.</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">alter</span> <span class="hljs-keyword">policy</span> <span class="hljs-string">"Enable read access for all users"</span>
  <span class="hljs-keyword">on</span> <span class="hljs-string">"public"</span>.<span class="hljs-string">"instructors"</span>
  <span class="hljs-keyword">to</span> <span class="hljs-keyword">public</span>
  <span class="hljs-keyword">using</span> (
    <span class="hljs-literal">true</span>
  );
</code></pre>
</li>
</ul>
<h3 id="heading-access-policy-for-the-students-table">Access Policy for the Students Table</h3>
<p>The <code>students</code> table has three access policies:</p>
<ul>
<li><p>Enable insert operation for authenticated users only.</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">alter</span> <span class="hljs-keyword">policy</span> <span class="hljs-string">"Enable insert for authenticated users only"</span>
  <span class="hljs-keyword">on</span> <span class="hljs-string">"public"</span>.<span class="hljs-string">"students"</span>
  <span class="hljs-keyword">to</span> <span class="hljs-keyword">authenticated</span>
  <span class="hljs-keyword">with</span> <span class="hljs-keyword">check</span> (
    <span class="hljs-literal">true</span>
  );
</code></pre>
</li>
<li><p>Enable update operation for authenticated users only.</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">alter</span> <span class="hljs-keyword">policy</span> <span class="hljs-string">"Enable update for only authenticated users"</span>
  <span class="hljs-keyword">on</span> <span class="hljs-string">"public"</span>.<span class="hljs-string">"students"</span>
  <span class="hljs-keyword">to</span> <span class="hljs-keyword">authenticated</span>
  <span class="hljs-keyword">using</span> ((auth.role() = <span class="hljs-string">'authenticated'</span>::<span class="hljs-built_in">text</span>))
</code></pre>
</li>
<li><p>Enable read access for authenticated users only.</p>
<pre><code class="lang-sql">  <span class="hljs-keyword">alter</span> <span class="hljs-keyword">policy</span> <span class="hljs-string">"Read access for only authenticated users"</span>
  <span class="hljs-keyword">on</span> <span class="hljs-string">"public"</span>.<span class="hljs-string">"students"</span>
  <span class="hljs-keyword">to</span> <span class="hljs-keyword">authenticated</span>
  <span class="hljs-keyword">using</span> (
    <span class="hljs-literal">true</span>
  );
</code></pre>
</li>
</ul>
<h2 id="heading-how-to-add-a-video-conferencing-feature-with-stream">How to Add a Video Conferencing Feature with Stream</h2>
<p>In this section, I'll walk you through adding a video conferencing feature to the application using the <a target="_blank" href="https://getstream.io/video/docs/react/">Stream Audio &amp; Video SDK</a>. This will enable instructors to schedule educational sessions and allow students to join the meetings.</p>
<h3 id="heading-setting-up-stream-video-amp-audio-sdk-in-nextjs">Setting Up Stream Video &amp; Audio SDK in Next.js</h3>
<p>Create a <a target="_blank" href="https://getstream.io/">Stream account</a> and a new organization that holds all your apps.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740657831403/ba044353-b0f4-4380-82cf-5abeedd68ac9.png" alt="Form for creating an organization, with fields for organization name, e-mail address, and website URL, and buttons labeled &quot;Cancel&quot; and &quot;Submit&quot;." class="image--center mx-auto" width="1635" height="527" loading="lazy"></p>
<p>Add a new app to the organization and copy the Stream API and Secret key into the <code>.env.local</code> file.</p>
<pre><code class="lang-bash">NEXT_PUBLIC_STREAM_API_KEY=&lt;paste_from_Stream_app_dashboard&gt;
STREAM_SECRET_KEY=&lt;paste_from_Stream_app_dashboard&gt;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740658025617/e8fb8a44-0a16-4730-875a-be3c2255276f.png" alt="Dashboard interface displaying chat overview with key metrics like Monthly Active Users (4 MAUs), Max Concurrent Connections (2), and Message Volume (3). Includes app access keys created on February 17th, 2025." class="image--center mx-auto" width="2527" height="1335" loading="lazy"></p>
<p>Create a new file named <code>stream.action.ts</code> inside the <code>actions</code> folder at the root of your Next.js project. This is the same folder where the authentication server actions for Supabase are stored. Then, copy the following code snippet into the file:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use server"</span>;

<span class="hljs-keyword">import</span> { getUserSession } <span class="hljs-keyword">from</span> <span class="hljs-string">"./auth"</span>;
<span class="hljs-keyword">import</span> { StreamClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@stream-io/node-sdk"</span>;

<span class="hljs-keyword">const</span> STREAM_API_KEY = process.env.NEXT_PUBLIC_STREAM_API_KEY!;
<span class="hljs-keyword">const</span> STREAM_API_SECRET = process.env.STREAM_SECRET_KEY!;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> tokenProvider = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> { user } = <span class="hljs-keyword">await</span> getUserSession();

    <span class="hljs-keyword">if</span> (!user) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"User is not authenticated"</span>);
    <span class="hljs-keyword">if</span> (!STREAM_API_KEY) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Stream API key secret is missing"</span>);
    <span class="hljs-keyword">if</span> (!STREAM_API_SECRET) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Stream API secret is missing"</span>);

    <span class="hljs-keyword">const</span> streamClient = <span class="hljs-keyword">new</span> StreamClient(STREAM_API_KEY, STREAM_API_SECRET);

    <span class="hljs-keyword">const</span> expirationTime = <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Date</span>.now() / <span class="hljs-number">1000</span>) + <span class="hljs-number">3600</span>;
    <span class="hljs-keyword">const</span> issuedAt = <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Date</span>.now() / <span class="hljs-number">1000</span>) - <span class="hljs-number">60</span>;

    <span class="hljs-keyword">const</span> token = streamClient.generateUserToken({
        user_id: user.id,
        exp: expirationTime,
        validity_in_seconds: issuedAt,
    });

    <span class="hljs-keyword">return</span> token;
};
</code></pre>
<ul>
<li><p>From the code snippet above,</p>
<ul>
<li><p>The <strong>getUserSession</strong> function returns the Supabase user object for the current user.</p>
</li>
<li><p>The <strong>tokenProvider</strong> function generates an authentication token for the user, enabling Stream to identify and manage users during real-time communication.</p>
</li>
</ul>
</li>
</ul>
<p>Create a <code>providers</code> folder containing a <code>StreamVideoProvider</code> component within the Next.js app folder and copy the following code snippet into the file:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> { createClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../../utils/supabase/client"</span>;
<span class="hljs-keyword">import</span> { tokenProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../../actions/stream.action"</span>;
<span class="hljs-keyword">import</span> { StreamVideo, StreamVideoClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@stream-io/video-react-sdk"</span>;
<span class="hljs-keyword">import</span> { useState, ReactNode, useEffect, useCallback } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { Loader2 } <span class="hljs-keyword">from</span> <span class="hljs-string">"lucide-react"</span>;

<span class="hljs-keyword">const</span> apiKey = process.env.NEXT_PUBLIC_STREAM_API_KEY!;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> StreamVideoProvider = <span class="hljs-function">(<span class="hljs-params">{ children }: { children: ReactNode }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [videoClient, setVideoClient] = useState&lt;StreamVideoClient | <span class="hljs-literal">null</span>&gt;(
        <span class="hljs-literal">null</span>
    );
    <span class="hljs-keyword">const</span> supabase = createClient();

    <span class="hljs-keyword">const</span> getUser = useCallback(<span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-comment">//👉🏻 get user object from Supabase</span>
        <span class="hljs-comment">//👉🏻 set Stream user data</span>
        <span class="hljs-comment">// 👉🏻 initialize Stream video client using the Stream API key, Stream user data, and token Provider</span>
    }, [supabase.auth]);

    useEffect(<span class="hljs-function">() =&gt;</span> {
        getUser();
    }, [getUser]);

    <span class="hljs-keyword">if</span> (!videoClient)
        <span class="hljs-keyword">return</span> (
            &lt;div className=<span class="hljs-string">'h-screen flex items-center justify-center'</span>&gt;
                &lt;Loader2 size=<span class="hljs-string">'32'</span> className=<span class="hljs-string">'mx-auto animate-spin'</span> /&gt;
            &lt;/div&gt;
        );

    <span class="hljs-keyword">return</span> &lt;StreamVideo client={videoClient}&gt;{children}&lt;/StreamVideo&gt;;
};
</code></pre>
<p>The <code>StreamVideoProvider</code> component is initialized and manages Stream’s video functionality across the application. It wraps all pages that require access to Stream's real-time video features. This includes:</p>
<ul>
<li><p><code>instructor/[id]</code> – displays an instructor’s upcoming sessions.</p>
</li>
<li><p><code>instructor/dashboard</code> – allows instructors to schedule new video calls.</p>
</li>
</ul>
<p>Update the <code>getUser</code> function as shown below:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> getUser = useCallback(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> { data, error } = <span class="hljs-keyword">await</span> supabase.auth.getUser();
    <span class="hljs-keyword">const</span> { user } = data;
    <span class="hljs-keyword">if</span> (error || !user || !apiKey) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">if</span> (!tokenProvider) <span class="hljs-keyword">return</span>;

    <span class="hljs-keyword">let</span> streamUser;

    <span class="hljs-keyword">if</span> (user.user_metadata?.image) {
        streamUser = {
            <span class="hljs-comment">// 👇🏻 user is an instructor</span>
            id: user.id,
            name: user.user_metadata?.name,
            image: user.user_metadata?.image,
        };
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// 👇🏻 user is a student</span>
        streamUser = {
            id: user.id,
            name: user.user_metadata?.name,
        };
    }

    <span class="hljs-comment">//👇🏻 create s Stream video client</span>
    <span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> StreamVideoClient({
        apiKey,
        user: streamUser,
        tokenProvider,
    });

    setVideoClient(client);
}, [supabase.auth]);
</code></pre>
<p>The <code>getUser</code> function retrieves the current user's data from Supabase Auth, sets up the Stream user, and initializes a Stream video client using the Stream API key, the user’s object and the token.</p>
<h3 id="heading-creating-and-scheduling-calls-with-stream">Creating and Scheduling Calls with Stream</h3>
<p>Here, you will learn how to allow instructors to schedule calls using the Stream Video &amp; Audio SDK.</p>
<p>Before we proceed, create a <code>hooks</code> folder within the Next.js app folder and add these files:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> app &amp;&amp; mkdir hooks
<span class="hljs-built_in">cd</span> hooks
touch useGetCallById.ts useGetCalls.ts
</code></pre>
<p>The <code>useGetCallById</code> file defines a React hook that fetches details of a specific Stream call via its ID, while the <code>useGetCalls</code> hook retrieves all calls created by a particular Stream user.</p>
<p>Let's create these custom React hooks.</p>
<p>Copy the following code snippet into the <code>useGetCallById.ts</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { Call, useStreamVideoClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@stream-io/video-react-sdk"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useGetCallById = <span class="hljs-function">(<span class="hljs-params">id: <span class="hljs-built_in">string</span> | <span class="hljs-built_in">string</span>[]</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [call, setCall] = useState&lt;Call&gt;();
    <span class="hljs-keyword">const</span> [isCallLoading, setIsCallLoading] = useState(<span class="hljs-literal">true</span>);

    <span class="hljs-keyword">const</span> client = useStreamVideoClient();

    useEffect(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">if</span> (!client) <span class="hljs-keyword">return</span>;

        <span class="hljs-keyword">const</span> loadCall = <span class="hljs-keyword">async</span> () =&gt; {
            <span class="hljs-keyword">try</span> {
                <span class="hljs-comment">// https://getstream.io/video/docs/react/guides/querying-calls/#filters</span>
                <span class="hljs-keyword">const</span> { calls } = <span class="hljs-keyword">await</span> client.queryCalls({
                    filter_conditions: { id },
                });

                <span class="hljs-keyword">if</span> (calls.length &gt; <span class="hljs-number">0</span>) setCall(calls[<span class="hljs-number">0</span>]);

                setIsCallLoading(<span class="hljs-literal">false</span>);
            } <span class="hljs-keyword">catch</span> (error) {
                <span class="hljs-built_in">console</span>.error(error);
                setIsCallLoading(<span class="hljs-literal">false</span>);
            }
        };

        loadCall();
    }, [client, id]);

    <span class="hljs-keyword">return</span> { call, isCallLoading };
};
</code></pre>
<p>Add the following to the <code>useGetCalls.ts</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { Call, useStreamVideoClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@stream-io/video-react-sdk"</span>;
<span class="hljs-keyword">import</span> { useParams } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/navigation"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> useGetCalls = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> client = useStreamVideoClient();
    <span class="hljs-keyword">const</span> [calls, setCalls] = useState&lt;Call[]&gt;();
    <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> { id } = useParams&lt;{ id: <span class="hljs-built_in">string</span> }&gt;();

    useEffect(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">const</span> loadCalls = <span class="hljs-keyword">async</span> () =&gt; {
            <span class="hljs-keyword">if</span> (!client || !id) <span class="hljs-keyword">return</span>;

            setIsLoading(<span class="hljs-literal">true</span>);

            <span class="hljs-keyword">try</span> {
                <span class="hljs-keyword">const</span> { calls } = <span class="hljs-keyword">await</span> client.queryCalls({
                    sort: [{ field: <span class="hljs-string">"starts_at"</span>, direction: <span class="hljs-number">1</span> }],
                    filter_conditions: {
                        starts_at: { $exists: <span class="hljs-literal">true</span> },
                        $or: [{ created_by_user_id: id }, { members: { $in: [id] } }],
                    },
                });

                setCalls(calls);
            } <span class="hljs-keyword">catch</span> (error) {
                <span class="hljs-built_in">console</span>.error(error);
            } <span class="hljs-keyword">finally</span> {
                setIsLoading(<span class="hljs-literal">false</span>);
            }
        };

        loadCalls();
    }, [client, id]);

    <span class="hljs-keyword">const</span> now = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>();
    <span class="hljs-comment">//👇🏻 upcoming calls</span>
    <span class="hljs-keyword">const</span> upcomingCalls = calls?.filter(<span class="hljs-function">(<span class="hljs-params">{ state: { startsAt } }: Call</span>) =&gt;</span> {
        <span class="hljs-keyword">return</span> startsAt &amp;&amp; <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(startsAt) &gt; now;
    });
    <span class="hljs-comment">//👇🏻 ongoing calls</span>
    <span class="hljs-keyword">const</span> ongoingCalls = calls?.filter(
        <span class="hljs-function">(<span class="hljs-params">{ state: { startsAt, endedAt } }: Call</span>) =&gt;</span> {
            <span class="hljs-keyword">return</span> startsAt &amp;&amp; <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(startsAt) &lt; now &amp;&amp; !endedAt;
        }
    );

    <span class="hljs-keyword">return</span> { upcomingCalls, isLoading, ongoingCalls };
};
</code></pre>
<p>The <strong>useGetCalls</strong> hook retrieves all calls where the instructor is either the creator or a participant, returning both current and upcoming calls. It also returns an <strong>isLoading</strong> state to indicate when data is being fetched, allowing for conditional rendering.</p>
<p>Add the function below to the instructor's dashboard to allow instructors to create or schedule calls. This function accepts a call description along with the scheduled date and time.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">//👇🏻 imports</span>
<span class="hljs-keyword">import</span> { useStreamVideoClient, Call } <span class="hljs-keyword">from</span> <span class="hljs-string">"@stream-io/video-react-sdk"</span>;
<span class="hljs-keyword">const</span> client = useStreamVideoClient();
<span class="hljs-comment">//👇🏻 Form states</span>
<span class="hljs-keyword">const</span> [description, setDescription] = useState&lt;<span class="hljs-built_in">string</span>&gt;(<span class="hljs-string">""</span>);
<span class="hljs-keyword">const</span> [dateTime, setDateTime] = useState&lt;<span class="hljs-built_in">string</span>&gt;(<span class="hljs-string">""</span>);

<span class="hljs-keyword">const</span> handleScheduleMeeting = <span class="hljs-keyword">async</span> (e: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {
    e.preventDefault();
    <span class="hljs-keyword">if</span> (!client || !user) <span class="hljs-keyword">return</span>;

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> id = crypto.randomUUID();
        <span class="hljs-keyword">const</span> call = client.call(<span class="hljs-string">"default"</span>, id);
        <span class="hljs-keyword">if</span> (!call) <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 create meeting"</span>);
    <span class="hljs-comment">//👇🏻 create Stream call</span>
        <span class="hljs-keyword">await</span> call.getOrCreate({
            data: {
                starts_at: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(dateTime).toISOString(),
                custom: {
                    description,
                },
            },
        });

        <span class="hljs-comment">//👇🏻 Call object</span>
        <span class="hljs-built_in">console</span>.log({ call });
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(error);
    }
};
</code></pre>
<p>The code snippet above initializes a Stream video call with a default call type. It assigns the call a unique ID, sets the scheduled date and time, and includes a custom description.</p>
<p><strong>Note:</strong> Ensure that the <code>&lt;StreamVideoProvider&gt;</code> component wraps the instructor's dashboard where the video call is being created. You can achieve this by adding a <code>layout.tsx</code> file to the dashboard page and wrapping all child elements with <code>&lt;StreamVideoProvider&gt;</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740658849436/109c53d7-c818-4d79-8bd7-19c3c0ae62a2.gif" alt="Dashboard interface titled &quot;LinkedUp&quot; with sections for followers, announcements, and options to make an announcement, schedule a call, or access the community channel. Logout button is visible at the top right." class="image--center mx-auto" width="800" height="443" loading="lazy"></p>
<h3 id="heading-joining-stream-video-calls">Joining Stream Video Calls</h3>
<p>The <code>instructor/[id]</code> page displays detailed information about a specific instructor from Supabase and lists of their current and upcoming calls. This allows students to view scheduled meetings and join them when they start.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740659135003/0c53b3b6-fa91-4d28-81cf-89e8c90b7e02.gif" alt="Screenshot of a profile page for Carl John, a UI Designer, on a platform called LinkedUp. It includes a profile picture, a button for joining a community channel, announcements with delete options, and upcoming meetings with join and copy link options." class="image--center mx-auto" width="800" height="443" loading="lazy"></p>
<p>To implement this functionality, we will use the <code>MeetingsBox</code> component within the instructor's profile page and create a dedicated <code>calls/[id]</code> page route for joining calls.</p>
<p>First, create a <code>(stream)</code> folder and add a <code>calls/[id]</code> page route. Then, create a <code>layout.tsx</code> file within the <code>(stream)</code> folder and insert the following code:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { StreamVideoProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">"../providers/StreamVideoProvider"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Metadata } <span class="hljs-keyword">from</span> <span class="hljs-string">"next"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> metadata: Metadata = {
    title: <span class="hljs-string">"Calls &amp; Chat | LinkedUp"</span>,
    description: <span class="hljs-string">"Generated by create next app"</span>,
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">AuthLayout</span>(<span class="hljs-params">{
    children,
}: Readonly&lt;{
    children: React.ReactNode;
}&gt;</span>) </span>{
    <span class="hljs-keyword">return</span> &lt;StreamVideoProvider&gt;{children}&lt;/StreamVideoProvider&gt;;
}
</code></pre>
<p>The <code>layout.tsx</code> file ensures that the <code>StreamVideoProvider</code> component wraps all pages inside the <code>(stream)</code> folder, enabling access to Stream's video and audio features across these pages.</p>
<p>Next, render the calls within the <a target="_blank" href="https://github.com/dha-stix/stream-lms/blob/main/src/app/instructor/%5Bid%5D/\(components\)/MeetingsBox.tsx">MeetingsBox component</a> and and let students join meetings.</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> { formatDateTime } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/lib/utils"</span>;
<span class="hljs-keyword">import</span> { Call } <span class="hljs-keyword">from</span> <span class="hljs-string">"@stream-io/video-react-sdk"</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">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MeetingsBox</span>(<span class="hljs-params">{
    upcomingCalls,
    isLoading,
    ongoingCalls,
}: {
    upcomingCalls: Call[] | <span class="hljs-literal">undefined</span>;
    isLoading: <span class="hljs-built_in">boolean</span>;
    ongoingCalls: Call[] | <span class="hljs-literal">undefined</span>;
}</span>) </span>{
    <span class="hljs-keyword">const</span> router = useRouter();

    <span class="hljs-keyword">if</span> (isLoading || !upcomingCalls || !ongoingCalls) {
        <span class="hljs-keyword">return</span> &lt;p className=<span class="hljs-string">'text-xs opacity-60'</span>&gt;Fetching calls...&lt;/p&gt;;
    }

    <span class="hljs-keyword">if</span> (upcomingCalls.length === <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">return</span> &lt;p className=<span class="hljs-string">'text-xs  opacity-60'</span>&gt;No upcoming meetings&lt;/p&gt;;
    }

    <span class="hljs-keyword">return</span> {
        <span class="hljs-comment">// --- upcoming and ongoing calls display elements ---</span>
    };
}
</code></pre>
<p>Return the following UI elements from the component to allow everyone to see the instructor's current and upcoming meetings.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">'space-y-4'</span>&gt;
        <span class="hljs-comment">// --- ongoing calls ---</span>
        {ongoingCalls.map(<span class="hljs-function">(<span class="hljs-params">call</span>) =&gt;</span> (
            &lt;div className=<span class="hljs-string">'bg-white p-2 rounded-md'</span> key={call.id}&gt;
                &lt;h3 className=<span class="hljs-string">'text-sm font-bold text-gray-500 mb-2'</span>&gt;
                    {call.state.custom.description}
                &lt;/h3&gt;
                &lt;p className=<span class="hljs-string">'text-xs'</span>&gt;
                    Started: {formatDateTime(call.state?.startsAt?.toLocaleString())}
                &lt;/p&gt;
                &lt;div className=<span class="hljs-string">'flex items-center space-x-4'</span>&gt;
                    &lt;button
                        className=<span class="hljs-string">'bg-blue-500 text-white px-4 py-2 text-xs rounded-md mt-2'</span>
                        onClick={<span class="hljs-function">() =&gt;</span> handleJoinCall(call)}
                    &gt;
                        Join In
                    &lt;/button&gt;

                    &lt;button
                        className=<span class="hljs-string">'bg-gray-500 text-white px-4 py-2 text-xs rounded-md mt-2'</span>
                        onClick={<span class="hljs-function">() =&gt;</span> handleCopyLink(call)}
                    &gt;
                        Copy Link
                    &lt;/button&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        ))}
        <span class="hljs-comment">// --- upcoming calls ---</span>
        {upcomingCalls.map(<span class="hljs-function">(<span class="hljs-params">call</span>) =&gt;</span> (
            &lt;div className=<span class="hljs-string">'bg-white p-2 rounded-md'</span> key={call.id}&gt;
                &lt;h3 className=<span class="hljs-string">'text-sm font-bold text-gray-500 mb-2'</span>&gt;
                    {call.state.custom.description}
                &lt;/h3&gt;

                &lt;div className=<span class="hljs-string">'flex items-center space-x-4'</span>&gt;
                    &lt;button
                        className=<span class="hljs-string">'bg-blue-500 text-white px-4 py-2 text-xs rounded-md mt-2'</span>
                        disabled={<span class="hljs-literal">true</span>}
                    &gt;
                        {formatDateTime(call.state?.startsAt?.toLocaleString())}
                    &lt;/button&gt;

                    &lt;button
                        className=<span class="hljs-string">'bg-gray-500 text-white px-4 py-2 text-xs rounded-md mt-2'</span>
                        onClick={<span class="hljs-function">() =&gt;</span> handleCopyLink(call)}
                    &gt;
                        Copy Link
                    &lt;/button&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        ))}
    &lt;/div&gt;
);
</code></pre>
<p>The <code>MeetingsBox</code> component renders the instructor's current and upcoming calls, allowing users to copy the call link and join meetings.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740659373366/9150c744-7bb0-4e1f-99f5-b6b89d2bc488.png" alt="Profile page for Carl John, a UI Designer, featuring announcements and upcoming meetings. Links to &quot;Join my Community Channel&quot; and meeting details are shown." class="image--center mx-auto" width="2560" height="1129" loading="lazy"></p>
<p>Execute the <code>handleJoinCall</code> function to redirect the user to the call page. This allows them to confirm the action before joining the call. The <code>handleCopyLink</code> function copies the call link to the clipboard.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> handleJoinCall = <span class="hljs-function">(<span class="hljs-params">call: Call</span>) =&gt;</span> {
    router.push(<span class="hljs-string">`/call/<span class="hljs-subst">${call.id}</span>`</span>);
};

<span class="hljs-keyword">const</span> handleCopyLink = <span class="hljs-function">(<span class="hljs-params">call: Call</span>) =&gt;</span> {
    navigator.clipboard.writeText(
        <span class="hljs-string">`<span class="hljs-subst">${process.env.NEXT_PUBLIC_PAGE_URL!}</span>/call/<span class="hljs-subst">${call.id}</span>`</span>
    );
    <span class="hljs-built_in">console</span>.log({
        title: <span class="hljs-string">"Link copied to clipboard"</span>,
        description: <span class="hljs-string">"You can now share the link with interested participants"</span>,
    });
};
</code></pre>
<p>Now, create the <code>call/[id]/page.tsx</code> component and copy the following code into the file:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> { useParams } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/navigation"</span>;
<span class="hljs-keyword">import</span> { useEffect, useState, useCallback } <span class="hljs-keyword">from</span> <span class="hljs-string">"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> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">"@supabase/supabase-js"</span>;
<span class="hljs-keyword">import</span> { createClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../../../../utils/supabase/client"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">CallPage</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> { id } = useParams&lt;{ id: <span class="hljs-built_in">string</span> }&gt;();
    <span class="hljs-keyword">const</span> [user, setUser] = useState&lt;User | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>);
    <span class="hljs-keyword">const</span> router = useRouter();

    <span class="hljs-keyword">const</span> authenticateUser = useCallback(<span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">const</span> supabase = createClient();
        <span class="hljs-keyword">const</span> { data } = <span class="hljs-keyword">await</span> supabase.auth.getUser();
        <span class="hljs-keyword">const</span> userData = data.user;
        <span class="hljs-keyword">if</span> (!userData) {
            <span class="hljs-keyword">return</span> router.push(<span class="hljs-string">"/student/auth/login"</span>);
        }
        setUser(userData);
    }, [router, call, camMicEnabled]);

    useEffect(<span class="hljs-function">() =&gt;</span> {
        authenticateUser();
    }, [authenticateUser]);

    <span class="hljs-keyword">return</span> {
        <span class="hljs-comment">// -- Conditionally render Stream Call component --</span>
    };
}
</code></pre>
<p>The code snippet authenticates the user to ensure they are signed in.</p>
<p>Next, fetch the call details using the call ID from the page route via the <code>useParams</code> hook.</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;
<span class="hljs-comment">//..other imports</span>
<span class="hljs-keyword">import</span> { useGetCallById } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/hooks/useGetCallById"</span>;
<span class="hljs-keyword">import</span> { StreamCall, StreamTheme } <span class="hljs-keyword">from</span> <span class="hljs-string">"@stream-io/video-react-sdk"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">CallPage</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">//..other states</span>
    <span class="hljs-keyword">const</span> { call, isCallLoading } = useGetCallById(id);
    <span class="hljs-keyword">const</span> [confirmJoin, setConfirmJoin] = useState&lt;<span class="hljs-built_in">boolean</span>&gt;(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> [camMicEnabled, setCamMicEnabled] = useState&lt;<span class="hljs-built_in">boolean</span>&gt;(<span class="hljs-literal">false</span>);

    <span class="hljs-keyword">const</span> handleJoin = <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-comment">//👇🏻 Stream join call function</span>
        call?.join();
        setConfirmJoin(<span class="hljs-literal">true</span>);
    };

    <span class="hljs-keyword">if</span> (isCallLoading) <span class="hljs-keyword">return</span> &lt;p&gt;Loading...&lt;/p&gt;;

    <span class="hljs-keyword">if</span> (!call) <span class="hljs-keyword">return</span> &lt;p&gt;Call not found&lt;/p&gt;;

    <span class="hljs-keyword">return</span> (
        &lt;main className=<span class="hljs-string">'min-h-screen w-full items-center justify-center'</span>&gt;
            &lt;StreamCall call={call}&gt;
                &lt;StreamTheme&gt;
                    {confirmJoin ? (
                        &lt;MeetingRoom call={call} /&gt;
                    ) : (
                        &lt;div className=<span class="hljs-string">'flex flex-col items-center justify-center gap-5 h-screen w-full'</span>&gt;
                            &lt;h1 className=<span class="hljs-string">'text-3xl font-bold'</span>&gt;Join Call&lt;/h1&gt;
                            &lt;p className=<span class="hljs-string">'text-lg'</span>&gt;
                                Are you sure you want to join <span class="hljs-built_in">this</span> call?
                            &lt;/p&gt;
                            &lt;div className=<span class="hljs-string">'flex gap-5'</span>&gt;
                                &lt;button
                                    onClick={handleJoin}
                                    className=<span class="hljs-string">'px-4 py-3 bg-blue-600 text-blue-50'</span>
                                &gt;
                                    Join
                                &lt;/button&gt;
                                &lt;button
                                    onClick={<span class="hljs-function">() =&gt;</span> router.back()}
                                    className=<span class="hljs-string">'px-4 py-3 bg-red-600 text-red-50'</span>
                                &gt;
                                    Cancel
                                &lt;/button&gt;
                            &lt;/div&gt;
                        &lt;/div&gt;
                    )}
                &lt;/StreamTheme&gt;
            &lt;/StreamCall&gt;
        &lt;/main&gt;
    );
}
</code></pre>
<p>In the code snippet above,</p>
<ul>
<li><p>The <a target="_blank" href="https://getstream.io/video/docs/react/ui-components/core/stream-call/"><strong>StreamCall</strong> component</a> wraps the entire call page, allowing access to various audio and video calling features. It accepts the <strong>call object</strong> as a prop.</p>
</li>
<li><p>The <a target="_blank" href="https://getstream.io/video/docs/react/ui-components/video-theme/"><strong>StreamTheme</strong> component</a> provides UI styling for the call, enabling you to use different themes.</p>
</li>
<li><p>The <code>confirmJoin</code> state is initially set to <code>false</code>. When the user clicks the <strong>Join</strong> button, it triggers the <code>handleJoin</code> function, which joins the call and updates <code>confirmJoin</code> to <code>true</code>.</p>
</li>
<li><p>When <code>confirmJoin</code> is <code>true</code>, the component renders the <code>MeetingRoom</code> component, which includes all prebuilt and customizable UI elements for the call provided by Stream.</p>
</li>
</ul>
<p>Finally, update the <code>authenticateUser</code> function to prompt the Stream user to enable or disable the camera and microphone immediately after joining a call.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">//👇🏻 call &amp; camera disable/enable state</span>
<span class="hljs-keyword">const</span> [camMicEnabled, setCamMicEnabled] = useState&lt;<span class="hljs-built_in">boolean</span>&gt;(<span class="hljs-literal">false</span>);

<span class="hljs-keyword">const</span> authenticateUser = useCallback(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> supabase = createClient();
    <span class="hljs-keyword">const</span> { data } = <span class="hljs-keyword">await</span> supabase.auth.getUser();
    <span class="hljs-keyword">const</span> userData = data.user;
    <span class="hljs-keyword">if</span> (!userData) {
        <span class="hljs-keyword">return</span> router.push(<span class="hljs-string">"/student/auth/login"</span>);
    }
    setUser(userData);
    <span class="hljs-comment">//👇🏻 Enable camera and microphone</span>
    <span class="hljs-keyword">if</span> (camMicEnabled) {
        call?.camera.enable();
        call?.microphone.enable();
    } <span class="hljs-keyword">else</span> {
        call?.camera.disable();
        call?.microphone.disable();
    }
}, [router, call, camMicEnabled]);

useEffect(<span class="hljs-function">() =&gt;</span> {
    authenticateUser();
}, [authenticateUser]);
</code></pre>
<h3 id="heading-stream-call-ui-components">Stream Call UI Components</h3>
<p>Stream makes setting up a call page easy using minimal UI components. It provides two prebuilt <a target="_blank" href="https://getstream.io/video/docs/react/ui-components/core/call-layout/">call layouts</a> (<strong>PaginatedGridLayout</strong> and <strong>SpeakerLayout)</strong> and a customizable <a target="_blank" href="https://getstream.io/video/docs/react/ui-cookbook/replacing-call-controls/"><strong>CallControls</strong> component.</a></p>
<ul>
<li><p>PaginatedGridLayout and SpeakerLayout define how call participants are displayed on the call page.</p>
</li>
<li><p>CallControls provides essential call functionalities such as toggling video and audio, sharing the screen, leaving the call, and more.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740659807590/b3e24561-60fc-4e0d-8054-a02d78a745a2.gif" alt="Video call interface with a person in a small window. The microphone is muted, indicated by a crossed-out microphone icon. Other controls and a red &quot;End Call for Everyone&quot; button are visible at the bottom." class="image--center mx-auto" width="800" height="442" loading="lazy"></p>
<p>Create the <strong>MeetingRoom</strong> component as follows:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> MeetingRoom = <span class="hljs-function">(<span class="hljs-params">{call} : {call: Call}</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [layout, setLayout] = useState&lt;CallLayoutType&gt;(<span class="hljs-string">"grid"</span>);
    <span class="hljs-keyword">const</span> router = useRouter();

<span class="hljs-comment">//👇🏻 allows members to leave the call</span>
    <span class="hljs-keyword">const</span> handleLeave = <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">if</span> (confirm(<span class="hljs-string">"Are you sure you want to leave the call?"</span>)) {
            router.push(<span class="hljs-string">"/"</span>);
        }
    };

<span class="hljs-comment">//👇🏻 describes the call layout</span>
    <span class="hljs-keyword">const</span> CallLayout = <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">switch</span> (layout) {
            <span class="hljs-keyword">case</span> <span class="hljs-string">"grid"</span>:
                <span class="hljs-keyword">return</span> &lt;PaginatedGridLayout /&gt;;
            <span class="hljs-keyword">case</span> <span class="hljs-string">"speaker-right"</span>:
                <span class="hljs-keyword">return</span> &lt;SpeakerLayout participantsBarPosition=<span class="hljs-string">'left'</span> /&gt;;
            <span class="hljs-keyword">default</span>:
                <span class="hljs-keyword">return</span> &lt;SpeakerLayout participantsBarPosition=<span class="hljs-string">'right'</span> /&gt;;
        }
    };

  <span class="hljs-keyword">return</span> (
    <span class="hljs-comment">//  -- Stream call UI component--</span>
  )
}
</code></pre>
<p>The <code>handleLeave</code> function enables call participants to leave the call and the <code>CallLayout</code> component determines how they are laid out on the screen.</p>
<p>Return the following from the <code>MeetingRoom</code> component:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">return</span> (
    &lt;section className=<span class="hljs-string">'relative min-h-screen w-full overflow-hidden pt-4'</span>&gt;
        &lt;div className=<span class="hljs-string">'relative flex size-full items-center justify-center'</span>&gt;
            &lt;div className=<span class="hljs-string">'flex size-full max-w-[1000px] items-center'</span>&gt;
                &lt;CallLayout /&gt;
            &lt;/div&gt;
            &lt;div className=<span class="hljs-string">'fixed bottom-0 flex w-full items-center justify-center gap-5'</span>&gt;
                &lt;CallControls onLeave={handleLeave} /&gt;
            &lt;/div&gt;

            &lt;div className=<span class="hljs-string">'fixed bottom-0 right-0 flex items-center justify-center gap-5 p-5'</span>&gt;
                &lt;EndCallButton call={call} /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/section&gt;
);
</code></pre>
<p>The CallLayout and CallControls components are rendered on the page, allowing users to communicate, share their screen, turn their camera on or off, and engage in conversations through reactions.</p>
<p>Finally, create the <strong>EndCallButton</strong> component to enable the host (instructor) to end the call for everyone.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">//👇🏻 Stream call hook</span>
<span class="hljs-keyword">import</span> { useCallStateHooks } <span class="hljs-keyword">from</span> <span class="hljs-string">"@stream-io/video-react-sdk"</span>;

<span class="hljs-keyword">const</span> EndCallButton = <span class="hljs-function">(<span class="hljs-params">{ call }: { call: Call }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { useLocalParticipant } = useCallStateHooks();
    <span class="hljs-keyword">const</span> localParticipant = useLocalParticipant();
    <span class="hljs-keyword">const</span> router = useRouter();

    <span class="hljs-keyword">const</span> participantIsHost =
        localParticipant &amp;&amp;
        call.state.createdBy &amp;&amp;
        localParticipant.userId === call.state.createdBy.id;

    <span class="hljs-keyword">if</span> (!participantIsHost) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;

    <span class="hljs-keyword">const</span> handleEndCall = <span class="hljs-function">() =&gt;</span> {
        call.endCall();
        <span class="hljs-built_in">console</span>.log({
            title: <span class="hljs-string">"Call Ended"</span>,
            description: <span class="hljs-string">"The call has been ended for everyone"</span>,
        });
        router.push(<span class="hljs-string">"/"</span>);
    };

    <span class="hljs-keyword">return</span> (
        &lt;button
            className=<span class="hljs-string">'bg-red-500 text-white px-4 py-2 rounded-md mt-2'</span>
            onClick={handleEndCall}
        &gt;
            End Call <span class="hljs-keyword">for</span> Everyone
        &lt;/button&gt;
    );
};
</code></pre>
<p>The code snippet above ensures that only the call host can end the call for all participants. It first checks if the current user is the host before displaying the <a target="_blank" href="https://getstream.io/video/docs/api/calls/#ending-calls">"End Call for Everyone" button</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740660039466/1851aba0-6961-4b54-87cf-54a3a98bb61a.gif" alt="Profile page of a UI Designer named Carl John. The page includes an announcement section with posts, an upcoming meetings section with join and copy link options, and a log out button. A button to join a community channel is also present." class="image--center mx-auto" width="800" height="442" loading="lazy"></p>
<h2 id="heading-how-to-integrate-a-group-chat-feature-using-stream-chat-messaging">How to Integrate a Group Chat Feature Using Stream Chat Messaging</h2>
<p>In this section, you will learn how to integrate a community chat feature into the application. Each instructor will create a group chat for their followers (students). The chat will allow students to interact with one another and share documents, video links, text, images, and so on using the <a target="_blank" href="https://getstream.io/chat/docs/sdk/react/">Stream Chat Messaging SDK</a>.</p>
<h3 id="heading-setting-up-the-stream-chat-sdk-in-nextjs">Setting Up the Stream Chat SDK in Next.js</h3>
<p>Add the following code snippet to the <code>stream.action.ts</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { StreamChat } <span class="hljs-keyword">from</span> <span class="hljs-string">"stream-chat"</span>;
<span class="hljs-keyword">import</span> { getUserSession } <span class="hljs-keyword">from</span> <span class="hljs-string">"./auth"</span>;

<span class="hljs-comment">//👇🏻 creates a StreamChat instance</span>
<span class="hljs-keyword">const</span> serverClient = StreamChat.getInstance(STREAM_API_KEY, STREAM_API_SECRET);

<span class="hljs-comment">//👇🏻 creates a token</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">createToken</span>(<span class="hljs-params"></span>): <span class="hljs-title">Promise</span>&lt;<span class="hljs-title">string</span>&gt; </span>{
    <span class="hljs-keyword">const</span> { user } = <span class="hljs-keyword">await</span> getUserSession();
    <span class="hljs-keyword">if</span> (!user) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"User is not authenticated"</span>);
    <span class="hljs-keyword">return</span> serverClient.createToken(user.id);
}
</code></pre>
<p>The code snippet above initializes a Stream Chat instance using its API key and secret key. It also includes a function that generates and returns a token based on the current user's ID.</p>
<p>To ensure that only instructors can create a community channel, follow these steps:</p>
<ol>
<li><p>Retrieve all the channels where the instructor is a member.</p>
</li>
<li><p>If no channels are found (i.e., the returned array is empty), the instructor can create a new channel.</p>
</li>
<li><p>An error message is displayed if a channel already exists, informing the instructor that they can only have one community channel.</p>
<pre><code class="lang-typescript"> <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">createChannel</span>(<span class="hljs-params">{
     userId,
     data,
 }: {
     userId: <span class="hljs-built_in">string</span>;
     data: { name: <span class="hljs-built_in">string</span>; imageUrl: <span class="hljs-built_in">string</span> };
 }</span>) </span>{
     <span class="hljs-keyword">try</span> {
         <span class="hljs-comment">//👇🏻 retrieve channel list</span>
         <span class="hljs-keyword">const</span> channels = <span class="hljs-keyword">await</span> serverClient.queryChannels(
             {
                 members: { $in: [userId] },
                 <span class="hljs-keyword">type</span>: <span class="hljs-string">"messaging"</span>,
             },
             { last_message_at: <span class="hljs-number">-1</span> }
         );
         <span class="hljs-comment">//👇🏻 instructor already has a channel</span>
         <span class="hljs-keyword">if</span> (channels.length &gt; <span class="hljs-number">0</span>) {
             <span class="hljs-keyword">return</span> {
                 success: <span class="hljs-literal">false</span>,
                 error: <span class="hljs-string">"You already have an existing channel"</span>,
                 id: channels[<span class="hljs-number">0</span>].id,
             };
         }
         <span class="hljs-comment">//👇🏻 declare channel type</span>
         <span class="hljs-keyword">const</span> channel = serverClient.channel(<span class="hljs-string">"messaging"</span>, <span class="hljs-string">`channel-<span class="hljs-subst">${userId}</span>`</span>, {
             name: data.name,
             image: data.imageUrl,
             members: [userId],
             created_by_id: userId,
         });
         <span class="hljs-comment">//👇🏻 create a channel</span>
         <span class="hljs-keyword">await</span> channel.create();
         <span class="hljs-keyword">return</span> { success: <span class="hljs-literal">true</span>, error: <span class="hljs-literal">null</span>, id: channel.id };
     } <span class="hljs-keyword">catch</span> (err) {
         <span class="hljs-keyword">return</span> { success: <span class="hljs-literal">false</span>, error: <span class="hljs-string">"Failed to create channel"</span>, id: <span class="hljs-literal">null</span> };
     }
 }
</code></pre>
</li>
</ol>
<p>The code snippet above <a target="_blank" href="https://getstream.io/chat/docs/react/creating_channels/">creates a public channel</a>, meaning anyone can join at any time. Also, the channel name is linked to the instructor's ID, ensuring it remains unique to that instructor.</p>
<p>To retrieve the instructor's channel link, add a function inside the <code>stream.action.ts</code> file. This function should return the channel URL (channel ID), allowing members to access the channel whenever needed. Then, you can display this link on the instructor's profile for easy access.</p>
<pre><code class="lang-typescript"><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">getInstructorChannel</span>(<span class="hljs-params">userId: <span class="hljs-built_in">string</span></span>) </span>{
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> channels = <span class="hljs-keyword">await</span> serverClient.queryChannels(
            {
                members: { $in: [userId] },
                <span class="hljs-keyword">type</span>: <span class="hljs-string">"messaging"</span>,
            },
            { last_message_at: <span class="hljs-number">-1</span> }
        );
        <span class="hljs-keyword">return</span> <span class="hljs-string">`/chat/<span class="hljs-subst">${channels[<span class="hljs-number">0</span>].id}</span>`</span>;
    } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    }
}
</code></pre>
<p>Finally, to grant users access to the channel page, check if the user is already a member. If not, add the student as a member before rendering the chat page. This ensures that only authorized users can participate in the conversation.</p>
<pre><code class="lang-typescript"><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">addUserToChannel</span>(<span class="hljs-params">channelId: <span class="hljs-built_in">string</span>, userId: <span class="hljs-built_in">string</span></span>) </span>{
    <span class="hljs-keyword">try</span> {
        <span class="hljs-comment">//👇🏻 check if student is already a member</span>
        <span class="hljs-keyword">const</span> channels = <span class="hljs-keyword">await</span> serverClient.queryChannels(
            {
                members: { $in: [userId] },
                <span class="hljs-keyword">type</span>: <span class="hljs-string">"messaging"</span>,
                id: channelId,
            },
            { last_message_at: <span class="hljs-number">-1</span> }
        );
        <span class="hljs-comment">//👇🏻 student already a member (success - show chat page)</span>
        <span class="hljs-keyword">if</span> (channels.length &gt; <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">return</span> {
                success: <span class="hljs-literal">true</span>,
                message: <span class="hljs-string">"Already a member"</span>,
                id: channels[<span class="hljs-number">0</span>].id,
                error: <span class="hljs-literal">null</span>,
            };
        }
        <span class="hljs-comment">//👇🏻 get channel by ID (student not a member)</span>
        <span class="hljs-keyword">const</span> channel = serverClient.channel(<span class="hljs-string">"messaging"</span>, channelId);
        <span class="hljs-comment">//👇🏻 add student to channel as a member</span>
        <span class="hljs-keyword">await</span> channel.addMembers([userId]);
        <span class="hljs-comment">//👇🏻 student now a member (success - show chat page)</span>
        <span class="hljs-keyword">return</span> {
            success: <span class="hljs-literal">true</span>,
            error: <span class="hljs-literal">null</span>,
            id: channel.id,
            message: <span class="hljs-string">"Member just added"</span>,
        };
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error adding user to channel:"</span>, error);
        <span class="hljs-keyword">return</span> {
            success: <span class="hljs-literal">false</span>,
            error: <span class="hljs-string">"Failed to add user to channel"</span>,
            id: <span class="hljs-literal">null</span>,
            message: <span class="hljs-literal">null</span>,
        };
    }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740660564180/474b342a-7d07-415e-b1d7-a213ed807b67.gif" alt="The Stream Chat page using the Stream Call UI components" class="image--center mx-auto" width="800" height="442" loading="lazy"></p>
<h3 id="heading-stream-chat-ui-components">Stream Chat UI Components</h3>
<p>Inside the <code>(stream)</code> folder, create a <code>chat/[id]/page.tsx</code> file. This page retrieves the channel ID from the page route and checks whether the user is already a channel member. If not, the user is automatically added as a member before displaying the chat interface.</p>
<p>Copy the following code snippet into the <code>chat/[id]/page.tsx</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> { useCallback, useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> StreamChat <span class="hljs-keyword">from</span> <span class="hljs-string">"./../(components)/StreamChat"</span>;
<span class="hljs-keyword">import</span> { useParams } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/navigation"</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">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ChatPage</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [userData, setUserData] = useState&lt;UserData | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>);
    <span class="hljs-keyword">const</span> [joinChannel, setJoinChannel] = useState&lt;<span class="hljs-built_in">boolean</span>&gt;(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> params = useParams&lt;{ id: <span class="hljs-built_in">string</span> }&gt;();
    <span class="hljs-keyword">const</span> router = useRouter();

    <span class="hljs-keyword">const</span> fetchUserData = useCallback(<span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-comment">// 👉🏻 get user object &amp; channel id from useParams</span>
        <span class="hljs-comment">// 👉🏻 execute the addUserToChannel() function declared in the previous section</span>
        <span class="hljs-comment">// 👉🏻 update the joinChannel React state</span>
    }, [params.id, router]);

    useEffect(<span class="hljs-function">() =&gt;</span> {
        fetchUserData();
    }, [fetchUserData]);

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

    <span class="hljs-keyword">return</span> (
        &lt;&gt;{joinChannel ? &lt;StreamChat user={userData} /&gt; : &lt;ConfirmMember /&gt;}&lt;/&gt;
    );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ConfirmMember</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">'flex flex-col items-center justify-center h-screen'</span>&gt;
            &lt;h1 className=<span class="hljs-string">'text-2xl font-bold mb-4 text-blue-500'</span>&gt;
                You are not a member <span class="hljs-keyword">of</span> <span class="hljs-built_in">this</span> channel
            &lt;/h1&gt;
            &lt;p className=<span class="hljs-string">'text-lg mb-4'</span>&gt;
                Please wait <span class="hljs-keyword">while</span> we add you to the channel
            &lt;/p&gt;

            &lt;div className=<span class="hljs-string">'loader'</span>&gt;
                &lt;Loader2 size={<span class="hljs-number">48</span>} className=<span class="hljs-string">'animate-spin'</span> /&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );
}
</code></pre>
<p>This code snippet ensures that a user is either already a member of the channel or is added before displaying the chat interface. The <strong>StreamChat</strong> component is a custom React component that contains all the Stream Chat UI elements. The <strong>ConfirmMember</strong> component shows a loading message while the user is added to the channel.</p>
<p>Create a StreamChat component and add the following imports to the file:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> { useCallback } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-comment">//👇🏻 -- Stream chat UI components</span>
<span class="hljs-keyword">import</span> {
    Chat,
    Channel,
    ChannelList,
    Window,
    ChannelHeader,
    MessageList,
    MessageInput,
    useCreateChatClient,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"stream-chat-react"</span>;
<span class="hljs-comment">// -- end of Stream chat UI components</span>

<span class="hljs-comment">//👇🏻 -- allows members to send emoji within the chat</span>
<span class="hljs-keyword">import</span> { EmojiPicker } <span class="hljs-keyword">from</span> <span class="hljs-string">"stream-chat-react/emojis"</span>;
<span class="hljs-keyword">import</span> { init, SearchIndex } <span class="hljs-keyword">from</span> <span class="hljs-string">"emoji-mart"</span>;
<span class="hljs-keyword">import</span> data <span class="hljs-keyword">from</span> <span class="hljs-string">"@emoji-mart/data"</span>;
init({ data });
<span class="hljs-comment">// -- end of emoji imports</span>
<span class="hljs-comment">//👇🏻 -- create token server action</span>
<span class="hljs-keyword">import</span> { createToken } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../../../../actions/stream.action"</span>;
</code></pre>
<p>Declare the StreamChat component as follows:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">StreamChat</span>(<span class="hljs-params">{ user }: { user: UserData }</span>) </span>{

    <span class="hljs-keyword">const</span> tokenProvider = useCallback(<span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> createToken();
    }, []);

    <span class="hljs-keyword">const</span> filters = { members: { $in: [user.id] }, <span class="hljs-keyword">type</span>: <span class="hljs-string">"messaging"</span> };
    <span class="hljs-keyword">const</span> options = { presence: <span class="hljs-literal">true</span>, state: <span class="hljs-literal">true</span> };

    <span class="hljs-keyword">const</span> client = useCreateChatClient({
        apiKey: process.env.NEXT_PUBLIC_STREAM_API_KEY!,
        tokenOrProvider: tokenProvider,
        userData: { id: user.id, name: user.name, image: user.image },
    });

    <span class="hljs-keyword">if</span> (!client) <span class="hljs-keyword">return</span> &lt;div&gt;Loading...&lt;/div&gt;;

  <span class="hljs-keyword">return</span> (
    <span class="hljs-comment">// -- Stream Chat UI components --</span>
  )
</code></pre>
<p>The <strong>useCreateChatClient</strong> hook creates a Stream chat client using the Stream API key, the user's data, and the token created using the <code>createToken()</code> function declared earlier in this section.</p>
<p>Finally, return the chat UI from the StreamChat component:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">return</span> (
    &lt;Chat client={client}&gt;
        &lt;div className=<span class="hljs-string">'chat-container'</span>&gt;
            {<span class="hljs-comment">/* -- Channel List -- */</span>}
            &lt;div className=<span class="hljs-string">'channel-list'</span>&gt;
                &lt;ChannelList
                    sort={{ last_message_at: <span class="hljs-number">-1</span> }}
                    filters={filters}
                    options={options}
                /&gt;
            &lt;/div&gt;

            {<span class="hljs-comment">/* -- Messages Panel -- */</span>}
            &lt;div className=<span class="hljs-string">'chat-panel'</span>&gt;
                &lt;Channel EmojiPicker={EmojiPicker} emojiSearchIndex={SearchIndex}&gt;
                    &lt;Window&gt;
                        &lt;ChannelHeader /&gt;
                        &lt;MessageList /&gt;
                        &lt;MessageInput /&gt;
                    &lt;/Window&gt;
                &lt;/Channel&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/Chat&gt;
);
</code></pre>
<ul>
<li><p>From the code snippet above:</p>
<ul>
<li><p><a target="_blank" href="https://getstream.io/chat/docs/sdk/react/components/core-components/chat/"><strong>Chat</strong> component</a> initializes the Stream Chat client and wraps the entire Chat page.</p>
</li>
<li><p><a target="_blank" href="https://getstream.io/chat/docs/sdk/react/components/core-components/channel_list/"><strong>ChannelList</strong></a> shows available chat channels.</p>
</li>
<li><p><a target="_blank" href="https://getstream.io/chat/docs/sdk/react/components/core-components/channel/"><strong>Channel</strong></a> sets up an active chat session.</p>
</li>
<li><p><strong>Window</strong> contains the message display and input areas.</p>
</li>
<li><p><strong>ChannelHeader</strong>, <a target="_blank" href="https://getstream.io/chat/docs/sdk/react/components/core-components/message_list/"><strong>MessageList</strong></a>, and <strong>MessageInput</strong> provide a fully functional chat interface.</p>
</li>
</ul>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1740660921805/65b237a8-584a-4d71-88ed-fe7edc68b737.png" alt="A screenshot of a group chat named &quot;UI Design Students&quot; with 2 members, showing a brief conversation with messages &quot;Hello&quot; and &quot;Hi,&quot; timestamped at 11:33 AM." class="image--center mx-auto" width="2560" height="1418" loading="lazy"></p>
<p>Congratulations! You've completed this tutorial. The <a target="_blank" href="https://github.com/dha-stix/stream-lms">source code for this article is also available on GitHub</a>.</p>
<h2 id="heading-next-steps">Next Steps</h2>
<p>So far, you've learned how to build a full-stack social learning platform using Stream and Supabase. This platform enables users to interact with one another through real-time chat powered by Stream.</p>
<p>Stream helps you build engaging apps that scale to millions with performant and flexible Chat, Video, Voice, Feeds, and Moderation APIs and SDKs powered by a global edge network and enterprise-grade infrastructure.</p>
<p>Here are some useful resources to help you get started:</p>
<ul>
<li><p><a target="_blank" href="https://getstream.io/chat/docs/">Stream Chat Documentation</a></p>
</li>
<li><p><a target="_blank" href="https://getstream.io/video/">Stream Video and Audio Documentation</a></p>
</li>
<li><p><a target="_blank" href="https://getstream.io/activity-feeds/">Stream Activity Feeds</a></p>
</li>
</ul>
<p>Thank you for reading! 🎉</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use FakeLogger to Make Testing Easier In .Net ]]>
                </title>
                <description>
                    <![CDATA[ When writing unit tests in .NET, you may need to verify that methods are logging exceptions, errors, or other key information. You might think, No problem, I'll just mock ILogger using my favourite mocking library – for example Moq, NSubstitute, or F... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-fakelogger-to-make-testing-easier-in-net/</link>
                <guid isPermaLink="false">67b3b736d8f6ac012e312ac5</guid>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Mon, 17 Feb 2025 22:24:54 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1739486043718/2d1e6339-fb93-4719-a89a-5b29e30c2bfc.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When writing unit tests in .NET, you may need to verify that methods are logging exceptions, errors, or other key information. You might think, <em>No problem, I'll just mock</em> <code>ILogger</code> <em>using my favourite mocking library</em> – for example Moq, NSubstitute, or FakeItEasy.</p>
<p>While <code>ILogger</code> itself is an interface and can be mocked, many of its commonly used logging methods (like <code>LogInformation()</code>, <code>LogError()</code>, and so on) are what’s called static or extension methods. Since static and extension methods can't be mocked directly, you often need a custom abstraction layer (LoggingService) or a decorator to pass to various other methods or services.</p>
<p>There is another much easier way though. In this article, I will show you how to use the relatively new feature available from .Net 8 upwards called <code>FakeLogger</code>.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-tutorial-setup">Tutorial Setup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-test-the-logging-functionality">How to Test the Logging Functionality</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-use-fakelogger">How to Use FakeLogger</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-installing-fakelogger-and-fluentassertions">Installing FakeLogger and FluentAssertions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-using-the-fakelogger-class">Using the FakeLogger Class</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-collector">What Is Collector?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-useful-collector-properties">Useful Collector Properties</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-assert-that-structured-log-arguments-are-passed-correctly">How to Assert That Structured Log Arguments Are Passed Correctly</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-verify-that-a-message-has-been-called-at-any-time">How to Verify that a Message Has Been Called at Any Time</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ol>
<h2 id="heading-tutorial-setup">Tutorial Setup</h2>
<p>Let’s imagine you’ve created an online shopping ordering and invoicing service. The logical code tests have been completed, but you now need to test the logging functionality.</p>
<p>For this tutorial we’ll be using the <code>OrderService</code> and <code>InvoiceService</code> classes defined below. I’ve provided comments to illustrate where normally your logic would go, but as this isn’t relevant for the purpose of this tutorial, comments will suffice.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">namespace</span> <span class="hljs-title">FakeLogger_Tutorial</span>;

<span class="hljs-function"><span class="hljs-keyword">public</span> class <span class="hljs-title">OrderService</span>(<span class="hljs-params">ILogger logger, IInvoiceService invoiceService</span>)</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ProcessOrder</span>(<span class="hljs-params">Order order</span>)</span>
    {
        logger.LogInformation(<span class="hljs-string">"Processing order..."</span>);

        <span class="hljs-comment">// Order processing code goes here</span>

        logger.LogInformation(<span class="hljs-string">"Order processed successfully."</span>);

        invoiceService.SendInvoice(order);
    }
}

<span class="hljs-function"><span class="hljs-keyword">public</span> class <span class="hljs-title">InvoiceService</span>(<span class="hljs-params">ILogger logger</span>) : IInvoiceService</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">SendInvoice</span>(<span class="hljs-params">Order order</span>)</span>
    {
        <span class="hljs-comment">// Dispatch order to shipping service</span>
        logger.LogInformation(<span class="hljs-string">"Order dispatched: {OrderId}"</span>, order.ID);

        <span class="hljs-comment">// Generate invoice code</span>

        SendEmail();
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">SendEmail</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Send email to customer</span>
        logger.LogInformation(<span class="hljs-string">"Sending invoice to customer"</span>);

        <span class="hljs-comment">// Perform email sending logic...</span>

        logger.LogInformation(<span class="hljs-string">"Email sent successfully."</span>);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IInvoiceService</span>
{
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">SendInvoice</span>(<span class="hljs-params">Order order</span>)</span>;
}
</code></pre>
<p>As well as a very basic <code>Order</code> and <code>Product</code> classes:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Order</span>
{
    <span class="hljs-keyword">public</span> Guid ID { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> required Guid CustomerId { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

    <span class="hljs-keyword">public</span> List&lt;Product&gt; Products = [];

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">decimal</span> TotalPrice =&gt; Products.Sum(x =&gt; x.Price);

    <span class="hljs-keyword">public</span> DateTime OrderDate { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Product</span>
{
    <span class="hljs-keyword">public</span> Guid ID { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Name { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">decimal</span> Price { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
</code></pre>
<h2 id="heading-how-to-test-the-logging-functionality">How to Test the Logging Functionality</h2>
<p>Like most aspects of coding, there are multiple ways to achieve this. The recommended approach is to mock the logger and assert against the mocked logger object rather than a concrete instance. This allows for controlled, isolated, and verifiable tests without relying on external dependencies or real logging behaviour – meaning cleaner and more maintainable tests.</p>
<p>You can do this using your preferred mocking library, such as <strong>Moq</strong>, <strong>FakeItEasy</strong>, or <strong>NSubstitute</strong>. You can learn more about these libraries and how to mock successfully in another tutorial I wrote, which you can find <a target="_blank" href="https://www.freecodecamp.org/news/explore-mocking-in-net/">here</a>.</p>
<p>Your initial thoughts may be to write tests like the below example using <code>Moq</code> and <code>XUnit</code> but this won’t work, and I’ll explain why.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> FakeLogger_Tutorial;
<span class="hljs-keyword">using</span> Microsoft.Extensions.Logging;
<span class="hljs-keyword">using</span> Moq;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">UnitTests</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">FailingTestCases</span>
{
    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">LogError_Should_Call_LogError</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> mockLogger = <span class="hljs-keyword">new</span> Mock&lt;ILogger&gt;();

        <span class="hljs-comment">// pass the mockedLogger to our service</span>
        <span class="hljs-keyword">var</span> orderService = <span class="hljs-keyword">new</span> OrderService(
            mockLogger.Object, 
            <span class="hljs-keyword">new</span> Mock&lt;IInvoiceService&gt;().Object
        );

        <span class="hljs-keyword">var</span> customerId = Guid.NewGuid();
        <span class="hljs-keyword">var</span> order = <span class="hljs-keyword">new</span> Order
        {
            ID = Guid.NewGuid(),
            CustomerId = customerId,
            Products = [<span class="hljs-keyword">new</span> Product { ID = Guid.NewGuid(), Name = <span class="hljs-string">"Ping pong balls"</span>, Price = <span class="hljs-number">1.00</span>M }],
            OrderDate = <span class="hljs-keyword">default</span>,
        };

        <span class="hljs-comment">// Act</span>
        orderService.ProcessOrder(order);      

        <span class="hljs-comment">// Assert</span>
        mockLogger.Verify(x =&gt; x.LogInformation(<span class="hljs-string">"Processing order..."</span>), Times.Once);
        mockLogger.Verify(x =&gt; x.LogInformation(<span class="hljs-string">"Order processed successfully."</span>), Times.Once);
    }
}
</code></pre>
<p>When you run this code, it <strong>will</strong> fail with the following error:</p>
<pre><code class="lang-markdown">System.NotSupportedException: 
Unsupported expression: x =&gt; x.LogInformation("Processing order...", new[] {  })
</code></pre>
<h3 id="heading-why-does-this-happen">Why Does This Happen?</h3>
<p>Mocking libraries struggle with static methods like <code>LogInformation</code> because they belong to the type itself, not an instance. Some tools, like JustMock, can handle this using advanced techniques like IL rewriting or shims, but these add complexity.</p>
<p>A common workaround is wrapping <code>ILogger</code> in a logging service for easier testing, along with benefits like abstraction and maintainability. But for a simpler approach, we’ll focus on the new <code>FakeLogger</code> class.</p>
<p>You could test ILogger using the <code>Verify</code> method in Moq, using some overly complicated, verbose methods like below. The test code will work, but it's a bit too complex and hard to read, especially at a glance.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> FakeLogger_Tutorial;
<span class="hljs-keyword">using</span> Microsoft.Extensions.Logging;
<span class="hljs-keyword">using</span> Moq;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">UnitTests</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">FailingTestCases</span>
{
    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">LogError_Should_Call_Logger_LogError</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> mockLogger = <span class="hljs-keyword">new</span> Mock&lt;ILogger&gt;();
        <span class="hljs-keyword">var</span> mockInvoiceService = <span class="hljs-keyword">new</span> Mock&lt;IInvoiceService&gt;();

        <span class="hljs-keyword">var</span> orderService = <span class="hljs-keyword">new</span> OrderService(
            mockLogger.Object, 
            mockInvoice.Object           
        );

        <span class="hljs-keyword">var</span> customerId = Guid.NewGuid();
        <span class="hljs-keyword">var</span> order = <span class="hljs-keyword">new</span> Order
        {
            ID = Guid.NewGuid(),
            CustomerId = customerId,
            Products = [<span class="hljs-keyword">new</span> Product { ID = Guid.NewGuid(), Name = <span class="hljs-string">"Ping pong balls"</span>, Price = <span class="hljs-number">1.00</span>M }],
            OrderDate = <span class="hljs-keyword">default</span>,
        };

        <span class="hljs-comment">// Act</span>
        orderService.ProcessOrder(order);

        <span class="hljs-comment">// Assert</span>
        mockLogger.Verify(logger =&gt; logger.Log(
                It.Is&lt;LogLevel&gt;(logLevel =&gt; logLevel == LogLevel.Information),
                It.Is&lt;EventId&gt;(eventId =&gt; eventId.Id == <span class="hljs-number">0</span>),
                It.Is&lt;It.IsAnyType&gt;((@object, @type) =&gt;
                    @object.ToString() == <span class="hljs-string">"Processing order..."</span>),
                It.IsAny&lt;Exception&gt;(),
                It.IsAny&lt;Func&lt;It.IsAnyType, Exception, <span class="hljs-keyword">string</span>&gt;&gt;()),
            Times.Once);
    }
}
</code></pre>
<h2 id="heading-how-to-use-fakelogger">How to Use FakeLogger</h2>
<p>With .NET 8, we can use the <code>FakeLogger</code> class to make tests clearer for other developers. If you haven’t upgraded yet, I highly recommend it—.NET 8 offers Long-Term Support (LTS) and unlocks many other useful features.</p>
<p>Microsoft defines the class as:</p>
<blockquote>
<p>This type is intended for use in unit tests. It captures all the log state to memory and lets you inspect it to validate that your code is logging what it should.</p>
</blockquote>
<p>In simple terms means that the FakeLogger acts as an in-memory collection of all the Logs and their associated data, meaning we can access these during out Unit Tests. It exposes all the extension methods we would find on the <code>ILogger</code> implementation, making it the perfect way to test our logging functionality.</p>
<h3 id="heading-installing-fakelogger-and-fluentassertions">Installing FakeLogger and FluentAssertions</h3>
<p>FluentAssertions is a great testing library which makes your code easier to test and easier to read. It focuses on using clearly named assertion functions, like <code>Should(), Have()</code> / <code>Be()</code>.</p>
<p>You can install using the Nuget Package Manager within your preferred IDE, or via the terminal with the following command:</p>
<pre><code class="lang-csharp">dotnet <span class="hljs-keyword">add</span> package FluentAssertions
</code></pre>
<p><strong>IMPORTANT: Do not exceed version 7.x.x of FluentAssertions, as v8 comes with a cost, whereas anything prior is free to use.</strong></p>
<p>Once installed, you will need to install <code>Microsoft.Extensions.Diagnostics.Testing</code> as before, using either the Package Console Manager, Terminal, or your preferred method.</p>
<pre><code class="lang-csharp">dotnet <span class="hljs-keyword">add</span> package Microsoft.Extensions.Diagnostics.Testing
</code></pre>
<h3 id="heading-using-the-fakelogger-class">Using the FakeLogger Class</h3>
<p>It is as simple as using any other class in C#. We can instantiate it like so:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> Microsoft.Extensions.Diagnostics.Testing;

<span class="hljs-keyword">var</span> fakeLogger = <span class="hljs-keyword">new</span> FakeLogger();
</code></pre>
<p>Now, rather than passing the <code>mockLogger.Object</code> to our OrderService as before, we shall instead pass our new <code>fakeLoger</code> object like so:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> loggingService = <span class="hljs-keyword">new</span> OrderService(fakeLogger);
</code></pre>
<p>Below is an example of how we can use <code>FakeLogger</code> to check if an <em>Information</em> message was logged.</p>
<pre><code class="lang-csharp">    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">OrderService_ProcessOrder_ShouldLogProgress</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> fakeLogger = <span class="hljs-keyword">new</span> FakeLogger();
        <span class="hljs-keyword">var</span> mockInvoiceService = <span class="hljs-keyword">new</span> Mock&lt;IInvoiceService&gt;();

        <span class="hljs-keyword">var</span> orderService = <span class="hljs-keyword">new</span> OrderService(
            fakeLogger,
            mockInvoiceService.Object
        );

        <span class="hljs-keyword">var</span> customerId = Guid.NewGuid();
        <span class="hljs-keyword">var</span> order = <span class="hljs-keyword">new</span> Order
        {
            ID = Guid.NewGuid(),
            CustomerId = customerId,
            Products = [<span class="hljs-keyword">new</span> Product { ID = Guid.NewGuid(), Name = <span class="hljs-string">"Ping pong balls"</span>, Price = <span class="hljs-number">1.00</span>M }],
            OrderDate = <span class="hljs-keyword">default</span>,
        };

        <span class="hljs-comment">// Act</span>
        orderService.ProcessOrder(order);

        <span class="hljs-comment">// Assert</span>
        fakeLogger.Collector.Count.Should().Be(<span class="hljs-number">2</span>);
        fakeLogger.Collector.LatestRecord.Level.Should().Be(LogLevel.Information);
        fakeLogger.Collector.LatestRecord.Message.Should().Be(<span class="hljs-string">"Order processed successfully."</span>);
    }
</code></pre>
<p>As you can see, it is much easier to read than the previous <code>Moq</code> implementation. The <code>FakeLogger</code> solution combined with <code>FluentAssertions</code> is much more concise and humanly readable to developers of all skillsets.</p>
<h3 id="heading-what-is-collector">What Is <code>Collector</code>?</h3>
<p>The <code>Collector</code> property in <code>FakeLogger</code> is an instance of <code>FakeLogCollector</code>, which collects and stores log information. It stores the messages in the same order they were called, making it easy to assert later.</p>
<h4 id="heading-purpose-of-the-collector-property"><strong>Purpose of the</strong> <code>Collector</code> Property</h4>
<ul>
<li><p>It <strong>stores all log messages</strong> captured by the <code>FakeLogger</code>.</p>
</li>
<li><p>You can access, filter, and assert against logs in your tests.</p>
</li>
<li><p>Useful when verifying structured logs or ensuring correct log levels.</p>
</li>
</ul>
<h3 id="heading-useful-collector-properties">Useful Collector Properties</h3>
<h3 id="heading-latestrecord"><code>LatestRecord</code></h3>
<p>There is more than one way in which you can access and assert logged messages. In the example above, we use the <code>LatestRecord</code> property. The <code>LatestRecord</code> property returns the last <code>FakeLogRecord</code> recorded. This comes from the internal property <code>Records</code>, returning the last record in the List.</p>
<p>The <code>FakeLogRecord</code> object has the following properties:</p>
<pre><code class="lang-csharp">Level
Id 
State
Exception
Message
Scopes
Category
LevelEnabled
Timestamp
</code></pre>
<p>We can therefore check any one of these properties in our assertions.</p>
<h3 id="heading-getsnapshot"><code>GetSnapshot()</code></h3>
<p>GetSnapshot() returns all log records collected.</p>
<ul>
<li><p>This method is useful when you want to inspect <strong>all</strong> logged messages, not just the most recent one.</p>
</li>
<li><p>It returns an <strong>immutable collection</strong>, ensuring that logs are not modified unexpectedly.</p>
</li>
</ul>
<p>As <code>GetSnapshot()</code> returns an immutable collection of messages. We can access these like any other collection of data, whilst also being able to use LINQ to filter, sort, and query the logs. This can be very useful when we would like to assert against the first, last, or any other logged message.</p>
<p>The following test utilises a concrete instance of <code>InvoiceService</code> as we wish to test the actual flow of logs, through both services.</p>
<pre><code class="lang-csharp">    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ProcessOrder_ShouldLogMultipleMessages</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> fakeLog = <span class="hljs-keyword">new</span> FakeLogger();
        <span class="hljs-keyword">var</span> invoiceService = <span class="hljs-keyword">new</span> InvoiceService(fakeLog);
        <span class="hljs-keyword">var</span> orderService = <span class="hljs-keyword">new</span> OrderService(fakeLog, invoiceService);
        <span class="hljs-keyword">var</span> testOrder = <span class="hljs-keyword">new</span> Order
        {
            ID = Guid.NewGuid(),
            CustomerId = Guid.NewGuid(),
            Products =
            [<span class="hljs-meta">
                new Product { ID = Guid.NewGuid(), Name = <span class="hljs-meta-string">"Product 1"</span>, Price = 99.99m },
                new Product { ID = Guid.NewGuid(), Name = <span class="hljs-meta-string">"Product 2"</span>, Price = 199.99m }
            </span>],
        };

        <span class="hljs-comment">// Act</span>
        orderService.ProcessOrder(testOrder);

        <span class="hljs-comment">// Assert</span>
        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">0</span>].Message.Should().Be(<span class="hljs-string">"Processing order..."</span>);
        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">0</span>].Level.Should().Be(LogLevel.Information);

        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">1</span>].Message.Should().Be(<span class="hljs-string">"Order processed successfully."</span>);
        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">1</span>].Level.Should().Be(LogLevel.Information);

        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">2</span>].Message.Should().Be(<span class="hljs-string">$"Order dispatched: <span class="hljs-subst">{testOrder.ID}</span>"</span>);
        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">2</span>].Level.Should().Be(LogLevel.Information);

        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">3</span>].Message.Should().Be(<span class="hljs-string">"Sending invoice to customer"</span>);
        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">3</span>].Level.Should().Be(LogLevel.Information);

        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">4</span>].Message.Should().Be(<span class="hljs-string">"Email sent successfully."</span>);
        fakeLog.Collector.GetSnapshot()[<span class="hljs-number">4</span>].Level.Should().Be(LogLevel.Information);
    }
</code></pre>
<p>This test demonstrates how straightforward it is to assert that the logger captures messages in execution order with the correct <code>LogLevel</code> and message. It also highlights the readability of the test.</p>
<h2 id="heading-how-to-assert-that-structured-log-arguments-are-passed-correctly"><strong>How to Assert That Structured Log Arguments Are Passed Correctly</strong></h2>
<p>Structured logging allows us to pass objects and variables as arguments to log messages, providing richer and more searchable logs. In <code>ILogger</code>, we can pass an object like this:</p>
<pre><code class="lang-csharp">_logger.LogInformation(<span class="hljs-string">"Order processed: {OrderId}"</span>, order.ID);
</code></pre>
<p>By default, logging providers (like the built-in .NET <code>ILogger</code> provider) replace placeholders immediately in the final log message.</p>
<p>With the built-in <code>ILogger</code>, the log message is fully formatted at runtime, for example:</p>
<pre><code class="lang-csharp">_logger.LogInformation(<span class="hljs-string">"Order number {OrderId} dispatched"</span>, <span class="hljs-number">123</span>);
</code></pre>
<p><strong>Final log recorded is:</strong></p>
<pre><code class="lang-csharp"><span class="hljs-string">"Order number 123 dispatched"</span>
</code></pre>
<p>This means that when retrieving logs in tests using the default log provider, we can only verify the final formatted string when using <code>FakeLogger</code> as it captures the fully rendered log message.</p>
<p><strong>Important:</strong> This differs from structured logging providers such as Serilog, where message templates and structured properties are stored separately. In Serilog, the <code>Message</code> column stores the original raw template string, while structured properties / objects are stored in a separate JSON field.</p>
<p>This doesn’t mean you can’t use <code>FakeLogger</code> with Serilog—you absolutely can. But when asserting logs, you must adjust your assertions depending on whether you're verifying the fully formatted message or structured properties.</p>
<p>If we log an order dispatch:</p>
<pre><code class="lang-csharp">logger.LogInformation(<span class="hljs-string">"Order dispatched: {OrderId}"</span>, order.ID);
</code></pre>
<p>Unlike Serilog, <code>FakeLogger</code> does not store <code>{OrderId}</code> as a separate property. Instead, it captures the fully formatted message:</p>
<pre><code class="lang-csharp"><span class="hljs-string">"Order dispatched: 550e8400-e29b-41d4-a716-446655440000"</span>
</code></pre>
<p>Thus, when testing with <code>FakeLogger</code>, we <strong>must</strong> assert against the final formatted string.</p>
<p>Even though <code>FakeLogger</code> does not store the original message template, it does capture structured data separately. This allows you to assert both:</p>
<ol>
<li><p>The final formatted message (since placeholders are replaced at runtime).</p>
</li>
<li><p>The structured data (objects or properties passed as arguments).</p>
</li>
</ol>
<p>The test below asserts the final formatted message, as well as a <code>StructuredState</code> object (the recorded structured log information).</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">InvoiceOrder_ShouldLog_StructuredLogInfo</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> fakeLogger = <span class="hljs-keyword">new</span> FakeLogger&lt;InvoiceService&gt;();
        <span class="hljs-keyword">var</span> service = <span class="hljs-keyword">new</span> InvoiceService(fakeLogger);
        <span class="hljs-keyword">var</span> testOrder = <span class="hljs-keyword">new</span> Order
        {
            ID = Guid.NewGuid(),
            CustomerId = Guid.NewGuid(),
            Products =
            [<span class="hljs-meta">
                new Product { ID = Guid.NewGuid(), Name = <span class="hljs-meta-string">"Product 1"</span>, Price = 99.99m },
                new Product { ID = Guid.NewGuid(), Name = <span class="hljs-meta-string">"Product 2"</span>, Price = 199.99m }
            </span>],
        };

        <span class="hljs-comment">// Act</span>
        service.SendInvoice(testOrder);

        <span class="hljs-comment">// Assert</span>
        fakeLogger.Collector.GetSnapshot()[<span class="hljs-number">0</span>].Message.Should().Be(<span class="hljs-string">$"Order dispatched: <span class="hljs-subst">{testOrder.ID}</span>"</span>);
        <span class="hljs-keyword">var</span> keyValuePairs = fakeLogger.Collector.GetSnapshot()[<span class="hljs-number">0</span>].StructuredState;

        <span class="hljs-keyword">var</span> orderIdProperty = keyValuePairs != <span class="hljs-literal">null</span> &amp;&amp; keyValuePairs
            .Any(x =&gt; x.Key == <span class="hljs-string">"OrderId"</span> &amp;&amp; x.Value == testOrder.ID.ToString());

        orderIdProperty.Should().BeTrue();
    }
</code></pre>
<h2 id="heading-how-to-verify-that-a-message-has-been-called-at-any-time">How to Verify That a Message Has Been Called at Any Time</h2>
<p>What if you want to test that a message or a set of messages are called <strong>anywhere</strong> within the call stack? You can easily do this with the help of LINQ (if you’re not familiar with LINQ you can read it about it in my other article <a target="_blank" href="https://www.freecodecamp.org/news/how-to-use-linq/">here</a>).</p>
<p>We don’t wish to assert that messages are sent in the correct order, just that the messages are logged. We can do this as follows:</p>
<pre><code class="lang-csharp">    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">AllMessages_Should_BeSentInAnyOrder</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> testOrder = <span class="hljs-keyword">new</span> Order
        {
            ID = Guid.NewGuid(),
            CustomerId = Guid.NewGuid(),
            Products =
            [<span class="hljs-meta">
                new Product { ID = Guid.NewGuid(), Name = <span class="hljs-meta-string">"Product 1"</span>, Price = 99.99m },
                new Product { ID = Guid.NewGuid(), Name = <span class="hljs-meta-string">"Product 2"</span>, Price = 199.99m }
            </span>],
        };

        <span class="hljs-keyword">var</span> fakeLogger = <span class="hljs-keyword">new</span> FakeLogger();
        <span class="hljs-keyword">var</span> invoiceService = <span class="hljs-keyword">new</span> InvoiceService(fakeLogger);
        <span class="hljs-keyword">var</span> orderService = <span class="hljs-keyword">new</span> OrderService(fakeLogger, invoiceService);
        <span class="hljs-keyword">var</span> expectedMessages = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt;
        {
            <span class="hljs-string">$"Order Dispatched: <span class="hljs-subst">{testOrder.ID}</span>"</span>,         
            <span class="hljs-string">"Processing order..."</span>,
            <span class="hljs-string">"Invoice sent"</span>
        };

        <span class="hljs-comment">// Act</span>
        orderService.ProcessOrder(testOrder);

        <span class="hljs-comment">// Assert</span>
        fakeLogger.Collector.GetSnapshot()
            .Select(x =&gt; x.Message)
            .Should().IntersectWith(expectedMessages);
    }
</code></pre>
<p>Here, we can utilise the power of LINQ and FluentAssertions to <code>Select</code> each message stored within the <code>Collector</code> property, and then assert that the array of messages can <code>IntersectWith</code> the expected messages.</p>
<p>The <code>IntersectWith</code> method asserts that the collection shares one or more items with the provided collection, a perfect fit for this kind of scenario where we don’t care about the order of logged messages – only that at some point they are logged.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>Testing logging in .NET applications has traditionally been tricky because of extension methods in <code>ILogger</code>. But with .NET 8’s <code>FakeLogger</code>, we now have a cleaner, more readable, and efficient way to verify log messages in unit tests.</p>
<p>By using <code>FakeLogger</code> alongside <code>FluentAssertions</code>, we can simplify assertions, improve test readability, and ensure our logging behaviour is correctly implemented without the complexity of traditional mocking libraries.</p>
<p>Whether you're verifying message content, structured logs, or execution order, <code>FakeLogger</code> provides a robust solution that integrates seamlessly into modern .NET testing practices. If you haven't already, I highly recommend upgrading to .NET 8 to take full advantage of this powerful feature.</p>
<p>Hope you found this helpful! If you want to chat more, feel free to reach out on <a target="_blank" href="https://x.com/grantdotdev">Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Collections in C# – Lists, Arrays, Dictionaries, and More ]]>
                </title>
                <description>
                    <![CDATA[ One of the first challenges beginners face when developing applications in C# is organising and managing data efficiently. Imagine keeping track of a list of items, mapping unique keys to values, or ensuring there are no duplicates in a collection – ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-collections-in-csharp/</link>
                <guid isPermaLink="false">6793a90ea69c4db6e76c194a</guid>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                    <category>
                        <![CDATA[ beginner ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Grant Riordan ]]>
                </dc:creator>
                <pubDate>Fri, 24 Jan 2025 14:51:58 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1737729136643/4cc12d37-da1c-45f0-928f-fbe02d7fdf52.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>One of the first challenges beginners face when developing applications in C# is organising and managing data efficiently. Imagine keeping track of a list of items, mapping unique keys to values, or ensuring there are no duplicates in a collection – these are all common tasks where choosing the right data structure can make a big difference.</p>
<p>C# provides a rich set of built-in data structures, such as <strong>lists</strong>, <strong>dictionaries</strong>, and more, making it easier to work with data differently. Each structure has strengths and is optimised for specific scenarios, so understanding their differences is key to writing clean, efficient, and maintainable code.</p>
<p>In this tutorial, we’ll explore:</p>
<ul>
<li><p><strong>Lists</strong>: Your go-to for dynamic, ordered collections where elements can grow and shrink effortlessly.</p>
</li>
<li><p><strong>Arrays</strong>: The efficient choice for fixed-size collections with predictable memory usage and blazing-fast indexing.</p>
</li>
<li><p><strong>Dictionaries</strong>: Perfect for quick lookups and managing key-value pairs with unmatched speed and clarity.</p>
</li>
<li><p><strong>Stacks</strong>: Ideal for last-in-first-out (LIFO) operations, like tracking history or nested structures.</p>
</li>
<li><p><strong>Queues</strong>: Best for first-in-first-out (FIFO) tasks, like processing jobs or managing sequential workflows.</p>
</li>
<li><p><strong>HashSets:</strong> The choice for collections where uniqueness matters and fast lookups are key.</p>
</li>
</ul>
<p>By the end of this guide, you'll understand the differences between these structures and be equipped to choose the right one for your next project.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-arrays">Arrays in C#</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-lists">Lists in C#</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-dictionaries">Dictionaries in C#</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-hashsets">HashSets in C#</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-queues">Queues in C#</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-stacks">Stacks in C#</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-common-problems">Common Problems</a></p>
</li>
</ol>
<p>For some of the following examples, you’ll need the <code>Animal</code> record below:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Animal</span>(<span class="hljs-params"><span class="hljs-keyword">int</span> Age, <span class="hljs-keyword">string</span> Name, <span class="hljs-keyword">int</span> Legs, <span class="hljs-keyword">string</span> Sound</span>)</span>;
</code></pre>
<h2 id="heading-arrays">Arrays</h2>
<p>An <strong>array</strong> in C# is a fixed-size collection of elements. Arrays are indexed, and their size is set when they are created unlike Lists and other collections. Once defined, the size of an array cannot be changed, making the memory efficient with a low overhead.</p>
<h3 id="heading-single-dimension-arrays">Single-Dimension Arrays</h3>
<p>Arrays are zero-index based, meaning their index begins at 0, rather than 1. If you’re not familiar, an index is a pointer to help you find an item.</p>
<p>For example, if you have 5 names in an Array, the first name is index [0], and the last name would be at index [4].</p>
<p>Arrays are great in scenarios where low-level performance is critical, as they have very little overhead due to their lack of metadata (additional attached information).</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">int</span>[] numbers = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[] { <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span> };

<span class="hljs-keyword">foreach</span>(<span class="hljs-keyword">var</span> number <span class="hljs-keyword">in</span> numbers){
    Console.Write(number);
}
<span class="hljs-comment">//Ouput</span>
<span class="hljs-comment">// 1 2 3 4 5</span>
</code></pre>
<p>In the above example, we instantiate an array with its values (thus giving it a fixed length). But we can assign values after the array is created by using index assignment.</p>
<p><em>Note: You must still specify the size of the array at the time of creation, as the code needs to know the fixed size of the Array.</em></p>
<pre><code class="lang-csharp"><span class="hljs-comment">// create an empty array of 20 indexes</span>
<span class="hljs-keyword">var</span> numbers = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">20</span>];

<span class="hljs-comment">// loop over available indexes and assign `i` </span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; numbers.Length; i++)
{
    numbers[i] = i + <span class="hljs-number">1</span>;
}

<span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">var</span> number <span class="hljs-keyword">in</span> numbers)
{
    Console.Write(<span class="hljs-string">$" <span class="hljs-subst">{number}</span>"</span>);
}
<span class="hljs-comment">// Output</span>
<span class="hljs-comment">//  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20</span>
</code></pre>
<h3 id="heading-multi-dimensional-arrays">Multi-Dimensional Arrays</h3>
<p>Arrays can also be multi-dimensional (for example, rows and columns), meaning they can hold two values. This makes them perfect for building grid-like structures.  </p>
<p>Unlike a jagged array, where each element is an array that can have different lengths, a multidimensional array is a matrix-like structure where each dimension has a fixed size.</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Create a 2D multi-dimensional array to represent a chessboard</span>

<span class="hljs-keyword">string</span>[,] chessBoard = <span class="hljs-keyword">new</span> <span class="hljs-keyword">string</span>[<span class="hljs-number">8</span>, <span class="hljs-number">8</span>];

<span class="hljs-comment">// Creating the starting positions of a Chessboard</span>
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>] = <span class="hljs-string">"Rook"</span>;
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">1</span>] = <span class="hljs-string">"Knight"</span>;
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">2</span>] = <span class="hljs-string">"Bishop"</span>;
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">3</span>] = <span class="hljs-string">"Queen"</span>;
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">4</span>] = <span class="hljs-string">"King"</span>;
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">5</span>] = <span class="hljs-string">"Bishop"</span>;
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">6</span>] = <span class="hljs-string">"Knight"</span>;
chessBoard[<span class="hljs-number">0</span>, <span class="hljs-number">7</span>] = <span class="hljs-string">"Rook"</span>;

chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">0</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">1</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">2</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">3</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">4</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">5</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">6</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">1</span>, <span class="hljs-number">7</span>] = <span class="hljs-string">"Pawn"</span>;

chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">0</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">1</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">2</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">3</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">4</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">5</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">6</span>] = <span class="hljs-string">"Pawn"</span>;
chessBoard[<span class="hljs-number">6</span>, <span class="hljs-number">7</span>] = <span class="hljs-string">"Pawn"</span>;

chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">0</span>] = <span class="hljs-string">"Rook"</span>;
chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">1</span>] = <span class="hljs-string">"Knight"</span>;
chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">2</span>] = <span class="hljs-string">"Bishop"</span>;
chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">3</span>] = <span class="hljs-string">"Queen"</span>;
chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">4</span>] = <span class="hljs-string">"King"</span>;
chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">5</span>] = <span class="hljs-string">"Bishop"</span>;
chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">6</span>] = <span class="hljs-string">"Knight"</span>;
chessBoard[<span class="hljs-number">7</span>, <span class="hljs-number">7</span>] = <span class="hljs-string">"Rook"</span>;


<span class="hljs-comment">// Print the chessboard</span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> row = <span class="hljs-number">0</span>; row &lt; <span class="hljs-number">8</span>; row++)
{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> col = <span class="hljs-number">0</span>; col &lt; <span class="hljs-number">8</span>; col++)
    {
        <span class="hljs-keyword">string</span> piece = chessBoard[row, col] ?? <span class="hljs-string">"Empty"</span>;
        Console.Write(<span class="hljs-string">$"<span class="hljs-subst">{piece}</span>\t"</span>);
    }
    Console.WriteLine();
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-bash">Rook    Knight  Bishop  Queen   King    Bishop  Knight  Rook    
Pawn    Pawn    Pawn    Pawn    Pawn    Pawn    Pawn    Pawn    
Empty   Empty   Empty   Empty   Empty   Empty   Empty   Empty   
Empty   Empty   Empty   Empty   Empty   Empty   Empty   Empty   
Empty   Empty   Empty   Empty   Empty   Empty   Empty   Empty   
Empty   Empty   Empty   Empty   Empty   Empty   Empty   Empty   
Pawn    Pawn    Pawn    Pawn    Pawn    Pawn    Pawn    Pawn    
Rook    Knight  Bishop  Queen   King    Bishop  Knight  Rook
</code></pre>
<h3 id="heading-jagged-array">Jagged Array</h3>
<p>Welcome to inception. A <strong>jagged array</strong> in C# is an array of arrays, where each "inner" array can have a different length.</p>
<p>Unlike multi-dimensional arrays, jagged arrays are not rectangular, meaning the rows can have varying sizes.</p>
<p>A usage example could be building a Calendar app. Below is a basic usage outputting the days of each month in the year:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">int</span>[][] daysInMonths = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">12</span>][];

<span class="hljs-comment">// Initialize each month with its corresponding number of days</span>
daysInMonths[<span class="hljs-number">0</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">31</span>]; <span class="hljs-comment">// January</span>
daysInMonths[<span class="hljs-number">1</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">28</span>]; <span class="hljs-comment">// February (non-leap year)</span>
daysInMonths[<span class="hljs-number">2</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">31</span>]; <span class="hljs-comment">// March</span>
daysInMonths[<span class="hljs-number">3</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">30</span>]; <span class="hljs-comment">// April</span>
daysInMonths[<span class="hljs-number">4</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">31</span>]; <span class="hljs-comment">// May</span>
daysInMonths[<span class="hljs-number">5</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">30</span>]; <span class="hljs-comment">// June</span>
daysInMonths[<span class="hljs-number">6</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">31</span>]; <span class="hljs-comment">// July</span>
daysInMonths[<span class="hljs-number">7</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">31</span>]; <span class="hljs-comment">// August</span>
daysInMonths[<span class="hljs-number">8</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">30</span>]; <span class="hljs-comment">// September</span>
daysInMonths[<span class="hljs-number">9</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">31</span>]; <span class="hljs-comment">// October</span>
daysInMonths[<span class="hljs-number">10</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">30</span>]; <span class="hljs-comment">// November</span>
daysInMonths[<span class="hljs-number">11</span>] = <span class="hljs-keyword">new</span> <span class="hljs-keyword">int</span>[<span class="hljs-number">31</span>]; <span class="hljs-comment">// December</span>

<span class="hljs-comment">// Print the number of days in each month</span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> month = <span class="hljs-number">0</span>; month &lt; daysInMonths.Length; month++)
{
    Console.WriteLine(<span class="hljs-string">$"Month <span class="hljs-subst">{month + <span class="hljs-number">1</span>}</span>: <span class="hljs-subst">{daysInMonths[month].Length}</span> days"</span>);
}
</code></pre>
<p>You should use an <code>Array</code> in:</p>
<ul>
<li><p><strong>Performance-critical applications</strong> where memory overhead and speed matter.</p>
</li>
<li><p><strong>Fixed data sets</strong> where the size will not change.</p>
</li>
<li><p><strong>Multi-dimensional data</strong>, for example, graph coordinates (x, y)</p>
</li>
</ul>
<h2 id="heading-lists">Lists</h2>
<p>A <code>List&lt;T&gt;</code> in C# is a resizable collection of items of the same type, signaled above by the letter <code>T</code>. It allows adding, removing, and accessing items by index. Unlike arrays, lists grow dynamically as needed.  </p>
<p>Commonly used for sequential data, they support <a target="_blank" href="https://www.freecodecamp.org/news/how-to-use-linq/">LINQ</a> queries and various utility methods for data manipulation.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> animals = <span class="hljs-keyword">new</span> List&lt;Animal&gt;()
{
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">10</span>, <span class="hljs-string">"Dog"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Woof"</span>),
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">5</span>, <span class="hljs-string">"Cat"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Meow"</span>),
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">2</span>, <span class="hljs-string">"Lion"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Roar"</span>),
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">6</span>, <span class="hljs-string">"Giraffe"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Trumpet"</span>),
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">15</span>, <span class="hljs-string">"Red-Panda"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Squeak"</span>)    
};

animals.Add(<span class="hljs-keyword">new</span> Animal(<span class="hljs-number">2</span>, <span class="hljs-string">"Hamster"</span>,<span class="hljs-number">4</span>,<span class="hljs-string">"Squeak"</span>));
animals.Remove(x=&gt;x.Sound == <span class="hljs-string">"Meow"</span>); <span class="hljs-comment">// Remove all squeaking animals</span>
</code></pre>
<p>Lists are a highly versatile data structure, where the order of items remains the same order in which they are added or removed (no manipulation. For example, whenever you call <code>.Add()</code> a method on a list, it will append the item to the list, and the order stays the same as before but with the additional animal.</p>
<p>You can modify the data (for example, filter, map, or sort) before sending lists to other areas of your application thanks to the extensive utility methods available in the <code>List&lt;T&gt;</code> class.</p>
<h2 id="heading-dictionaries">Dictionaries</h2>
<p>Dictionaries work just like the term we know in the English language.</p>
<p>We have a key (a lookup term) and a value (the mapped object or data). Because of this, you might hear the term 'key-value pair' when referring to dictionaries.</p>
<p>Dictionaries are best used to efficiently retrieve data based on a unique identifier, such as an ID, name, or other uniquely identifying fields. They ensure their unique keys are ideal for scenarios requiring optimal performance without iterative searching.  </p>
<p>I recommend using dictionaries when the order of elements is unimportant and you need to represent relationships, such as mapping countries to capitals, products to prices, or people to addresses.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> animalDictionary = <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, Animal&gt;()
{
    { <span class="hljs-string">"Dog"</span>, <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">10</span>, <span class="hljs-string">"Dog"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Woof"</span>) },
    { <span class="hljs-string">"Cat"</span>, <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">5</span>, <span class="hljs-string">"Cat"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Meow"</span>) },
    { <span class="hljs-string">"Elephant"</span>, <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">8</span>, <span class="hljs-string">"Elephant"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Trumpet"</span>) },
    { <span class="hljs-string">"Lion"</span>, <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">2</span>, <span class="hljs-string">"Lion"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Roar"</span>) },
    { <span class="hljs-string">"Giraffe"</span>, <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">6</span>, <span class="hljs-string">"Giraffe"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Trumpet"</span>) },
};
<span class="hljs-comment">// Add</span>
animalDictionary.Add(<span class="hljs-string">"Red panda"</span>, <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">2</span>, <span class="hljs-string">"Red Panda"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Squeaker"</span>));

<span class="hljs-comment">// Remove</span>
animalDictionary.Remove(<span class="hljs-string">"Cat"</span>);

<span class="hljs-comment">//Get</span>
<span class="hljs-keyword">var</span> giraffe = animalDictionary[<span class="hljs-string">"Giraffe"</span>];
</code></pre>
<h2 id="heading-hashsets">HashSets</h2>
<p>A <code>HashSet&lt;T&gt;</code> is a collection in C# that stores unique elements. It uses a hash-based implementation to ensure very efficient lookups, additions, and deletions. This means it uses hash functions to quickly map keys to values, you can read more about that here.</p>
<p>Duplicate elements are automatically ignored.</p>
<p>How does this differ from a Dictionary? HashSets don't have keys like Dictionaries. Instead, they store values directly and are accessed by iterating over the elements using a <code>foreach</code> loop or LINQ queries.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> animalHashSet = <span class="hljs-keyword">new</span> HashSet&lt;Animal&gt;()
{
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">3</span>, <span class="hljs-string">"Lion"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Roar"</span>),
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">5</span>, <span class="hljs-string">"Tiger"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Roar"</span>),
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">2</span>, <span class="hljs-string">"Elephant"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Trumpet"</span>),
    <span class="hljs-keyword">new</span> Animal(<span class="hljs-number">1</span>, <span class="hljs-string">"Giraffe"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Neigh"</span>)
};
<span class="hljs-comment">// Add</span>
animalHashSet.Add(<span class="hljs-keyword">new</span> Animal(<span class="hljs-number">3</span>, <span class="hljs-string">"Lion"</span>, <span class="hljs-number">4</span>, <span class="hljs-string">"Roar"</span>));
<span class="hljs-comment">// Remove</span>
animalHashSet.Remove(x=&gt;x.Sound == <span class="hljs-string">"Neigh"</span>);
<span class="hljs-comment">// Get</span>
animalHashSet.FirstOrDefault(x=&gt;x.Name == <span class="hljs-string">"Elephant"</span>);
</code></pre>
<p>Above, we create a <code>HashSet&lt;Animal&gt;</code> and attempt to add a duplicate object. You may expect this to throw an error, as we know HashSets can only store unique values. But instead it handles it quite beautifully and simply doesn’t add the duplicate object, so the output is:</p>
<pre><code class="lang-bash">Animal { Age = 3, Name = Lion, Legs = 4, Sound = Roar }
Animal { Age = 5, Name = Tiger, Legs = 4, Sound = Roar }
Animal { Age = 2, Name = Elephant, Legs = 4, Sound = Trumpet }
</code></pre>
<h2 id="heading-queues">Queues</h2>
<p>Queues work in just the same way as a queue does in everyday life, with a first-in, first-out approach.</p>
<p><code>Queue&lt;T&gt;</code> does not implement the ICollection interface like Dictionaries and Lists, meaning it doesn't have an <strong>Add()</strong> method. This means you cannot add elements to the Queue whilst instantiating. It also means you cannot use the <strong>Add()</strong> method to add items – instead, you use the <strong>Enqueue()</strong> method.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> arc = <span class="hljs-keyword">new</span> Queue&lt;<span class="hljs-keyword">string</span>&gt;();
arc.Enqueue(<span class="hljs-string">"2 Lions"</span>);
arc.Enqueue(<span class="hljs-string">"2 Tigers"</span>);
arc.Enqueue(<span class="hljs-string">"2 Bears"</span>);

<span class="hljs-comment">// Peek method allows to peek at the front of the queue</span>
Console.WriteLine(<span class="hljs-string">"Front of the Queue: "</span> + arc.Peek());

<span class="hljs-comment">// Output</span>
<span class="hljs-comment">// Front of the Queue: 2 Lions</span>

Console.WriteLine(<span class="hljs-string">$"Processing: <span class="hljs-subst">{arc.Dequeue()}</span>"</span>); <span class="hljs-comment">// Output: 2 Lions</span>
Console.WriteLine(<span class="hljs-string">$"Processing: <span class="hljs-subst">{arc.Dequeue()}</span>"</span>); <span class="hljs-comment">// Output: 2 Tigers</span>
</code></pre>
<p>The <code>Dequeue</code> method not only returns the next item in the queue but also removes the item from the queue as expected. You can also clear the queue using the <code>Clear()</code> method.</p>
<h2 id="heading-stacks">Stacks</h2>
<p>Stacks work oppositely to <code>Queue&lt;T&gt;</code>, in that instead of first-in-first-out, they work on a last-in-first-out mechanic.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> stack = <span class="hljs-keyword">new</span> Stack&lt;<span class="hljs-keyword">int</span>&gt;();
stack.Push(<span class="hljs-number">1</span>);
stack.Push(<span class="hljs-number">2</span>);
stack.Push(<span class="hljs-number">3</span>);
stack.Push(<span class="hljs-number">4</span>);
stack.Push(<span class="hljs-number">5</span>);

<span class="hljs-comment">// iterate over the Stack</span>
<span class="hljs-keyword">foreach</span>(<span class="hljs-keyword">var</span> number <span class="hljs-keyword">in</span> stack )
{
    Console.WriteLine(number);
}

<span class="hljs-comment">// Output</span>
<span class="hljs-comment">// 5 4 3 2 1</span>
</code></pre>
<p>You may think looping through the items in a <code>Stack</code> would work the same as a List or Queue and would still print them out in order of going in. But the system knows it’s a stack, and so it enumerates the items in reverse order of how they were added – that is, the most recently added element (<code>5</code>) is returned first.</p>
<p>You can also utilise the <code>Pop()</code> method which will return the last item in the collection, and remove it at the same time.</p>
<h2 id="heading-common-problems">Common Problems</h2>
<p>When using various collections, you will more than likely come across common problems, such as <code>KeyNotFoundException</code> when using dictionaries, <code>IndexOutOfRangeException</code> on lists/arrays, or <code>InvalidOperationException</code> when modifying a collection during iteration.</p>
<h3 id="heading-keynotfoundexception"><code>KeyNotFoundException</code></h3>
<p><strong>Scenario:</strong> You try to access a Dictionary key that doesn’t exist in the Dictionary. This will result in a <code>KeyNotFoundException</code>, and error.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> dictionary = <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>&gt;()
{
    { <span class="hljs-string">"Morning"</span>, <span class="hljs-string">"Good Morning"</span> },
    { <span class="hljs-string">"Afternoon"</span>, <span class="hljs-string">"Good afternoon"</span> },
    { <span class="hljs-string">"Evening"</span>, <span class="hljs-string">"Good evening"</span> },
    { <span class="hljs-string">"Night"</span>, <span class="hljs-string">"Good night"</span> },
};

<span class="hljs-keyword">var</span> message = dictionary[<span class="hljs-string">"Dusk"</span>];

Console.WriteLine(message);

<span class="hljs-comment">// Output</span>
<span class="hljs-comment">//Unhandled exception. System.Collections.Generic.KeyNotFoundException: The given key 'Dusk' was not present in the dictionary.</span>
</code></pre>
<p><strong>Solution:</strong> I recommend using the <code>TryGetValue</code> function, which will handle it gracefully and return the item as an <code>out</code> parameter (if it can be found, otherwise the default value).</p>
<p><code>TryGetValue</code> returns a boolean to show whether it could or couldn’t find the provided key. This boolean value can then be utilised to determine functionality based on successful retrieval or not, rather than checking the output parameter, for example (if it is null/empty or not).</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> dictionary = <span class="hljs-keyword">new</span> Dictionary&lt;<span class="hljs-keyword">string</span>, <span class="hljs-keyword">string</span>&gt;()
{
    { <span class="hljs-string">"Morning"</span>, <span class="hljs-string">"Good Morning"</span> },
    { <span class="hljs-string">"Afternoon"</span>, <span class="hljs-string">"Good afternoon"</span> },
    { <span class="hljs-string">"Evening"</span>, <span class="hljs-string">"Good evening"</span> },
    { <span class="hljs-string">"Night"</span>, <span class="hljs-string">"Good night"</span> },
};

<span class="hljs-keyword">var</span> input = Console.ReadLine(); <span class="hljs-comment">// Dusk</span>

dictionary.TryGetValue(input, <span class="hljs-keyword">out</span> <span class="hljs-keyword">var</span> message);
Console.WriteLine(<span class="hljs-string">$"`Message:<span class="hljs-subst">{message}</span>`"</span>);
<span class="hljs-comment">// Ouput = `Message ` (blank message as default string value is empty string</span>

<span class="hljs-comment">//or check if was able to retrieve and access output if it was</span>
<span class="hljs-keyword">if</span>(dictionary.TryGetValue(input,<span class="hljs-keyword">out</span> <span class="hljs-keyword">var</span> m)){
    Console.WriteLine(message);
}
</code></pre>
<h3 id="heading-indexoutofrangeexception-listsarrays"><code>IndexOutOfRangeException</code> (Lists/Arrays):</h3>
<p><strong>Scenario</strong>: Trying to access an index that’s outside the valid range of a list or array.  </p>
<p>As we know, Arrays are 0 index-based, so trying to access an index of [5] on an Array of 5 items will throw the <code>IndexOutOfRangeException</code>.</p>
<p><strong>Solutions:</strong></p>
<ol>
<li>Ensure the index is within bounds using <code>list.Count</code> or <code>array.Length</code> before access.</li>
</ol>
<ol start="2">
<li>Use the <code>ElementAtOrDefault()</code> method. If it can’t access an item at the given index, it will return the default value, which can then be handled accordingly.</li>
</ol>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> names = <span class="hljs-keyword">new</span> <span class="hljs-keyword">string</span>[]
{
    <span class="hljs-string">"Tony"</span>, <span class="hljs-string">"Clint"</span>, <span class="hljs-string">"Bob"</span>, <span class="hljs-string">"Alice"</span>, <span class="hljs-string">"Lisa"</span>
};

<span class="hljs-keyword">var</span> name = names.ElementAtOrDefault(<span class="hljs-number">6</span>);
Console.WriteLine(name ?? <span class="hljs-string">"Name not found."</span>);
</code></pre>
<h3 id="heading-invalidoperationexception-iterating-collections"><code>InvalidOperationException</code> (Iterating Collections):</h3>
<p><strong>Scenario</strong>: Modifying a collection (for example, adding or removing items) while iterating over it with a <code>foreach</code> loop will throw an <code>InvalidOperationException</code> because you're trying to remove an item from the list while iterating over it with a <code>foreach</code> loop.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> myList = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt; { <span class="hljs-string">"Apple"</span>, <span class="hljs-string">"Banana"</span>, <span class="hljs-string">"Cherry"</span>, <span class="hljs-string">"Banana"</span> };

<span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">var</span> item <span class="hljs-keyword">in</span> myList)
{
    <span class="hljs-keyword">if</span> (item == <span class="hljs-string">"Banana"</span>)
    {
        myList.Remove(item); <span class="hljs-comment">// Throws InvalidOperationException</span>
    }
}
</code></pre>
<h4 id="heading-why-this-happens"><strong>Why This Happens:</strong></h4>
<ul>
<li><p>The <code>foreach</code> loop maintains an internal enumerator for the collection.</p>
</li>
<li><p>Modifying the collection (for example, adding/removing items) invalidates the enumerator, which causes the runtime to throw an <code>InvalidOperationException</code>.</p>
</li>
</ul>
<p><strong>Solution 1: Use a</strong> <code>for</code> <strong>Loop</strong></p>
<p>You can use a <code>for</code> loop with an index to safely modify the list during iteration:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> fruits = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt; { <span class="hljs-string">"Apple"</span>, <span class="hljs-string">"Banana"</span>, <span class="hljs-string">"Cherry"</span>, <span class="hljs-string">"Banana"</span> };

<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; fruits; i++)
{
    <span class="hljs-keyword">if</span> (fruits[i] == <span class="hljs-string">"Banana"</span>)
    {
        fruits.RemoveAt(i);
        <span class="hljs-comment">// Adjust the index to account for the removed item</span>
        i--; 
    }
}
Console.WriteLine(<span class="hljs-keyword">string</span>.Join(<span class="hljs-string">", "</span>, fruits)); 
<span class="hljs-comment">// Output: Apple, Cherry</span>
</code></pre>
<p><strong>Solution 2: Iterate Over a Copy</strong></p>
<p>Another approach is to iterate over a copy of the list using <code>ToList()</code>. This way, you’re not directly iterating over the original collection, so modifications won’t affect the loop.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> originalList = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt; { <span class="hljs-string">"Apple"</span>, <span class="hljs-string">"Banana"</span>, <span class="hljs-string">"Cherry"</span>, <span class="hljs-string">"Banana"</span> };

<span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">var</span> item <span class="hljs-keyword">in</span> originalList.ToList()) <span class="hljs-comment">// Create a copy</span>
{
    <span class="hljs-keyword">if</span> (item == <span class="hljs-string">"Banana"</span>)
    {
        originalList.Remove(item); <span class="hljs-comment">// Safe removal</span>
    }
}
Console.WriteLine(<span class="hljs-keyword">string</span>.Join(<span class="hljs-string">", "</span>, originalList)); 
<span class="hljs-comment">// Output: Apple, Cherry</span>
</code></pre>
<p><strong>Solution 3: Use LINQ to Filter</strong></p>
<p>If you only want to remove items based on a condition, you can use LINQ to create a new filtered list:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> fruits = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt; { <span class="hljs-string">"Apple"</span>, <span class="hljs-string">"Banana"</span>, <span class="hljs-string">"Cherry"</span>, <span class="hljs-string">"Banana"</span> };

fruits = fruits.Where(item =&gt; item != <span class="hljs-string">"Banana"</span>).ToList(); <span class="hljs-comment">// Filter out "Banana"</span>

Console.WriteLine(<span class="hljs-keyword">string</span>.Join(<span class="hljs-string">", "</span>, fruits)); 
<span class="hljs-comment">// Output: Apple, Cherry</span>
</code></pre>
<h2 id="heading-closing-thoughts">Closing Thoughts</h2>
<p>In this article, you’ve learned about many of the common Data Structures for storing multiple objects and values.</p>
<p>Whether you're storing data in a fixed-size array, managing a dynamic list, working with first-in-first-out queues, last-in-first-out stacks, or key-value pair dictionaries, knowing when and how to use each collection is key to becoming a confident and proficient C# developer.</p>
<p>Mastering these concepts will not only improve your ability to handle data effectively but also lay the groundwork for more advanced topics in data structures and algorithms. Combining these data structures with LINQ can provide some performant and easy-to-use mechanics. To learn more about LINQ you can check out my article <a target="_blank" href="https://www.freecodecamp.org/news/how-to-use-linq/">here</a>.  </p>
<p>As you continue your coding journey, keep experimenting with these collections, apply them in real-world scenarios, and deepen your understanding of their inner workings.</p>
<p>As always should you wish to discuss this article further, any other coding-related problems, or hear about other articles I’m writing, drop me a follow on <a target="_blank" href="https://x.com/grantdotdev">X(Twitter)</a>  </p>
<p>Happy coding! 😊</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
