<?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[ Ankur Tyagi - 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[ Ankur Tyagi - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 09:04:41 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/TheAnkurTyagi/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Use Vibe Coding Effectively as a Dev ]]>
                </title>
                <description>
                    <![CDATA[ It may seem like everyone is a vibe coder these days, and prompting seemed like it would become the new coding. But is this AI-generated code really deployable? Bragging on social media about a clever script is one thing, but pushing a vibe coded app... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-vibe-coding-effectively-as-a-dev/</link>
                <guid isPermaLink="false">6925deb0b459e862808eb04c</guid>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Programming Blogs ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vibe coding ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ankur Tyagi ]]>
                </dc:creator>
                <pubDate>Tue, 25 Nov 2025 16:52:00 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1764089459731/0122c0b7-08e2-434a-b5eb-518025401951.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>It may seem like everyone is a vibe coder these days, and prompting seemed like it would become the new coding. But is this AI-generated code really deployable?</p>
<p>Bragging on social media about a clever script is one thing, but pushing a vibe coded app to prod comes with many security risks.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758881769141/9bedc585-5608-4660-a304-bbb10f10b8f2.png" alt="Vibe-debug, vibe-refactor, and vibe-check" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>With so many AI dev tools out there now, <a target="_blank" href="https://www.freecodecamp.org/news/how-to-perform-code-reviews-in-tech-the-painless-way/">code reviews</a> become more critical than ever.</p>
<p>This article will explore what <strong>vibe coding</strong> means and how code reviews should adapt in the era of AI.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents:</strong></h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-vibe-coding">What is Vibe Coding?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-implement-vibe-coding-in-practice">How to Implement Vibe Coding in Practice</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-isnt-vibe-coded-output-production-ready">Why isn’t Vibe Coded Output Production Ready?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-context-gaps-are-the-first-crack">Context gaps are the first crack.</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-those-gaps-lead-directly-to-integration-blind-spots">Those gaps lead directly to integration blind spots.</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-most-serious-risk-is-security-by-omission">The most serious risk is security by omission.</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-testing-and-correctness-evidence-are-thin">Testing and correctness evidence are thin.</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-operability-lags-behind">Operability lags behind.</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-guidelines-for-ai-code-reviews">Guidelines for AI Code Reviews</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-code-review-process-in-vibe-coding">Code Review Process in Vibe Coding</a></li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-checklist-for-reviewing-ai-generated-code">Checklist for Reviewing AI Generated Code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-work-effectively-with-ai-tools">How to Work Effectively with AI Tools</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-what-is-vibe-coding">What is Vibe Coding?</h2>
<p>In early 2025, AI researcher <a target="_blank" href="https://x.com/karpathy">Andrej Karpathy</a> popularized the term vibe coding to describe a new way of development in which you “fully give in to the vibes” and let AI write code while you focus on high level intent.</p>
<p>A developer expresses their desired functionality in plain language, and an AI system (like an <a target="_blank" href="https://en.wikipedia.org/wiki/Large_language_model">LLM</a>) generates the source code to implement it.</p>
<p>This code-by-prompt approach allows even beginners to produce working code without deep knowledge of programming languages. Karpathy joked that with advanced IDE agents (like <a target="_blank" href="https://www.devtoolsacademy.com/blog/cursor-vs-windsurf/">Cursor’s</a> Composer mode), “I barely even touch the keyboard... I ‘Accept All’ always, I don’t read the diffs anymore... and it mostly works”.</p>
<p>So, vibe coding is coding by vibe and trusting AI to handle the heavy lifting.</p>
<h2 id="heading-how-to-implement-vibe-coding-in-practice">How to Implement Vibe Coding in Practice</h2>
<p>In practice, vibe coding usually involves using AI assistants and adapting your workflow to a more interactive, prompt-driven style.</p>
<p>Here’s an overview of how you can “vibe code” a project:</p>
<h3 id="heading-step-1-choose-an-ai-assistant">Step 1: Choose an AI assistant</h3>
<p>Select a development env that supports AI code generation. Popular choices include <a target="_blank" href="https://cursor.com/">Cursor</a> and <a target="_blank" href="https://github.com/features/copilot">GitHub Copilot</a>.</p>
<h3 id="heading-step-2-define-your-requirements">Step 2: Define your requirements</h3>
<p>Instead of writing boilerplate code, describe what you want to build. Provide AI with a specific prompt detailing functionality. The more <a target="_blank" href="https://www.philschmid.de/context-engineering">context</a> and detail you give, the better AI can fulfill your intent.</p>
<p>For example, when I ran an SEO inspection for my website, DevTools Academy, I used this prompt in Cursor:</p>
<blockquote>
<p>“Now, act as a senior product engineer and UX strategist. Evaluate and improve <a target="_blank" href="https://www.devtoolsacademy.com">https://www.devtoolsacademy.com</a> with a practical, no-fluff lens.</p>
<p>Scope:</p>
<ul>
<li><p>UX</p>
</li>
<li><p>SEO and technical SEO</p>
</li>
<li><p>Positioning and messaging</p>
</li>
<li><p>Copywriting and information architecture</p>
</li>
<li><p>What to add to stand out in the developer tools space.”</p>
</li>
</ul>
</blockquote>
<p>This prompt works well because it gives the AI a clear role, a defined scope, and a specific intent. AI knows it’s not just fixing SEO but also reviewing how the site communicates value to devs. That combination of clarity and context produces actionable insights instead of surface-level suggestions.</p>
<p>Below is a screenshot of that audit in progress and showing how I reviewed code, metadata, and UX recommendations side by side.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761258218099/91e93726-1a7d-4d1a-9839-531355037dfc.png" alt="cursor screenshot showing CodeRabbit reviewing a pull request with comments and summary." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>You can checkout the full code on my open source <a target="_blank" href="https://github.com/tyaga001/devtoolsacademy">blog</a> here and check out closed PRs. This will help you learn how I use all these coding agents on a production ready app.</p>
<h3 id="heading-step-3-review-the-code">Step 3: Review the code</h3>
<p>AI will produce initial code based on your prompt. Think of this as a prototype – it’s not perfect. Run the code and see how it behaves.</p>
<p>Let’s look at an example: here, CodeRabbit is reviewing one of my <a target="_blank" href="https://github.com/tyaga001/devtoolsacademy/pull/145">pull requests</a> on GitHub. I had pushed a small fix to sort blog posts correctly and make sure the RSS feed reflects the latest publish date. Within seconds, CodeRabbit analyzed the diff, understood the intent behind my change, and explained exactly what the new code does.</p>
<p>It pointed out that the fix now sorts posts before mapping them, uses the sorted data for both items and the lastBuildDate, and ensures proper chronological order throughout the feed.</p>
<p>It’s like having a senior reviewer who not only checks syntax but also validates logic and confirms that your reasoning holds up.</p>
<p><a target="_blank" href="https://github.com/tyaga001/devtoolsacademy/pull/145"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758879621613/95bee1e7-3953-4416-b48b-e844332be950.png" alt="GitHub pull request showing CodeRabbit review comments on code changes with highlighted fixes." class="image--center mx-auto" width="600" height="400" loading="lazy"></a></p>
<p>This is just a reminder to expect imperfections. Vibe coding embraces a <em>“code first, refine later”</em> mindset. This means you get a working version quickly, then iteratively improve it. You might go through a few cycles of prompt -&gt; code -&gt; test -&gt; tweak.</p>
<h3 id="heading-step-4-validate-debug-polish">Step 4: Validate, debug, polish</h3>
<p>Once AI generated code meets your expectations, do a final review.</p>
<p>Throughout the process, the core idea is that you collaborate with the AI. The AI agent serves as a coding assistant, making real-time suggestions, automating tedious boilerplate, and even generating entire modules on your behalf.</p>
<h2 id="heading-why-isnt-vibe-coded-output-production-ready">Why Isn’t Vibe Coded Output Production Ready?</h2>
<p>Vibe coding moves fast: you describe intent, the AI produces something that runs, and you’re off to the next prompt. What’s missing is the slow, unglamorous work that usually turns a draft into shippable software, like shared context, architectural alignment, verification, and documentation.</p>
<p>AI generates plausible code based on patterns it has seen. But it doesn’t understand your team’s history, your system’s constraints, or the implicit rules that keep everything coherent over time.</p>
<p>That mismatch shows up the moment a “works on my machine” demo meets a real codebase.</p>
<p>Let’s explore the common pitfalls of vibe-coded code, so you’ll know what to watch for. Then, in the checklist section below, I’ll outline practical strategies to address or prevent each issue.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758271815928/5f763a0f-2dda-4318-8c19-0c9e58447abe.png" alt="AI is Limited by Context" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-context-gaps-are-the-first-crack">Context gaps are the first crack.</h3>
<p>AI only sees what you show it, so it’s easy for it to make the right local choice and the wrong global one: duplicating logic that already exists, choosing defaults that conflict with prior decisions, or introducing functions that don’t respect domain boundaries.</p>
<p>The result is code that looks reasonable in isolation but collides with existing assumptions and conventions once integrated.</p>
<h3 id="heading-those-gaps-lead-directly-to-integration-blind-spots">Those gaps lead directly to integration blind spots.</h3>
<p>Drafts often ignore the lived details of your environment – shared utilities, cross-cutting concerns, configuration, deployment hooks, and operational policies. Interfaces may line up at a glance and still fail at runtime because the draft doesn’t fit how your system composes modules, handles errors, or manages state across services.</p>
<h3 id="heading-the-most-serious-risk-is-security-by-omission">The most serious risk is security by omission.</h3>
<p>AI rarely includes robust input validation, clear authentication and authorization paths, or rate limiting unless you spell it out. Secrets handling and logging tend to be superficial or missing. That leaves common exposure points like request handlers, job processors, and webhook endpoints without the checks that prevent injection, SSRF, mass assignment, or data exfiltration.</p>
<p>Even when the surface looks tidy, the absence of explicit security controls means you’re trusting defaults you didn’t choose.</p>
<h3 id="heading-testing-and-correctness-evidence-are-thin">Testing and correctness evidence are thin.</h3>
<p>Quality suffers in quieter ways, too. Beyond “it runs,” there’s little to demonstrate behavior across edge cases or to guard against regressions.</p>
<p>Performance and scalability remain unknowns: extra network calls, N+1 patterns, and quadratic loops sneak in because nobody measured them. Dependencies and environments drift as versions aren’t pinned, infrastructure isn’t declared, and configuration lives only in the author’s head, making behavior differ across machines and CI.</p>
<h3 id="heading-operability-lags-behind">Operability lags behind.</h3>
<p>A lack of metrics, missing health/readiness probes, and no runbook make failures harder to detect and slower to recover from. Add in data quality and compliance concerns (PII handling, encoding assumptions, transitive license obligations), and you have code that demos well but isn’t ready for production’s reliability, security, and audit demands.</p>
<p>In short, vibe-coded output accelerates drafting but skips the shared understanding and evidence that make software safe to ship.</p>
<p>Until those gaps are closed, it’s a prototype, not a release.</p>
<h2 id="heading-guidelines-for-ai-code-reviews">Guidelines for AI Code Reviews</h2>
<p>Your team should keep pre-AI engineering standards as the bar, including security, tests, readability, maintainability, performance, and docs. AI should change how fast you gather the evidence for those standards, not how much evidence you require. In other words, use AI to accelerate the path to your existing bar, never to lower it.</p>
<p>Using AI, you can generate code at speed. But if reviews take the same amount of time (or more time), you lose some of the benefit. The goal isn’t to relax standards, it’s to shorten the time to prove you met them. That means layering in automation (tests, static analysis, secret scans, SCA) and AI-assisted review to catch obvious issues quickly so human reviewers can focus on intent, architecture, and risk.</p>
<p>Well-used assistants can help here. For example, tools like CodeRabbit, GitHub Copilot PR Reviewer, Claude Code, Cursor’s Bugbot, Graphite’s AI Review, and Greptile can highlight potential bugs, security gaps, style deviations, and mismatched intent, and summarize diffs for faster context. Treat these as accelerators for your existing process, not as replacements for judgment.</p>
<h3 id="heading-code-review-process-in-vibe-coding">Code Review Process in Vibe Coding</h3>
<p>The fundamentals of good code reviews haven’t changed – and in fact, they’re more critical now.</p>
<p>Below are some key principles to maintain speed without sacrificing quality.</p>
<h4 id="heading-1-trust-but-verify">1. Trust, but verify.</h4>
<p>A reviewer usually assumes the author understands the system. With vibe-coded output, the “author” may be an AI with limited context. If something looks odd or unnecessary, question it. Run the code, add/execute tests, or ask the developer/AI for clarification on intent and constraints.</p>
<h4 id="heading-2-dont-let-reviews-become-a-bottleneck">2. Don’t let reviews become a bottleneck.</h4>
<p>Vibe coding generates code quickly. If human review takes as long as hand-writing the change, you’ve erased the gain.</p>
<p>Combat this by front-loading automation: run unit/integration tests, static analysis (lint/SAST), secret scans, SCA, and basic perf checks in CI to clear the noise. Then reviewers spend their time on design trade-offs, boundary cases, and risk. The balance is: high standards, faster evidence.</p>
<h4 id="heading-3-use-ai-code-reviews-wisely">3. Use AI code reviews wisely</h4>
<p>AI can help review code just as it helps generate it. Modern “pair reviewer” tools scan a PR and surface likely bugs, security issues, missing tests, or style violations in minutes plus give natural-language summaries of the change.</p>
<p>Tools you can consider include CodeRabbit, GitHub Copilot PR Reviewer, Claude Code, Cursor Bugbot, Graphite, and Greptile. Many integrate with the CLI/IDE and GitHub/GitLab to leave actionable comments.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758272500586/a9cc891f-ab1a-47d8-a607-a772cbaef2e0.png" alt="coderabbit CLI" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Think of them as fast first-pass reviewers that increase coverage and consistency across PRs.</p>
<h4 id="heading-4-human-judgment-is-still-irreplaceable">4. Human judgment is still irreplaceable.</h4>
<p>Even the best AI reviewer is an assistant. Keep humans accountable for correctness, security posture, architectural fit, and user impact. A healthy pattern is AI first-pass &gt; human second-pass that inspects invariants, failure modes, and long-term maintainability.</p>
<h4 id="heading-5-maintain-a-high-bar-for-quality">5. Maintain a high bar for quality.</h4>
<p>It’s tempting to accept “it runs” when an AI wrote it. Don’t. Stakeholders still expect software to be robust, secure, and maintainable. Keep DRY, readability, and testability standards. Insist on input validation, authZ checks where relevant, and sensible logging/metrics. If you can’t provide evidence that you met the bar, you haven’t met it.</p>
<h4 id="heading-6-educate-and-document">6. Educate and document</h4>
<p>When reviewers find bugs or security flaws in AI-generated code, capture the lesson.</p>
<p>Update internal guides with patterns like “When generating handlers, validate and bound inputs, add rate limits, log request IDs, avoid N+1 queries, and sanitize user-visible output.” Over time, bake these into prompts, templates, repo scaffolds, and CI checks so the next AI draft starts closer to done.</p>
<h2 id="heading-checklist-for-reviewing-ai-generated-code">Checklist for Reviewing AI Generated Code</h2>
<p>Before approving any vibe-coded change, make the standards explicit and verifiable. Use this checklist to confirm behavior, security, performance, integration, and documentation so the draft you got from AI becomes code you can safely ship.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762510535966/85ea547a-f955-446b-9e22-965dc18f9e49.png" alt="Checklist for Reviewing AI Generated Code" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Here’s a checklist a human reviewer should go through before approving vibe-coded output:</p>
<h3 id="heading-1-define-the-codes-purpose-scope-amp-non-goals">1. Define the code’s purpose (scope &amp; non-goals).</h3>
<p>Be explicit about what this change does and does not do. Tie it to a user story/ticket and call out non-goals so “helpful” AI changes don’t creep in.</p>
<h3 id="heading-2-verify-x-and-y-behavior-and-edge-cases">2. Verify X and Y (behavior and edge cases).</h3>
<p>Be clear about what you’re verifying. For example, verify input parsing and pagination boundaries, verify that error paths return the correct status and body, and verify that database writes are idempotent. Run existing tests, add missing unit/integration tests, and reproduce edge inputs (empty, null, huge, unicode).</p>
<h3 id="heading-3-perform-code-quality-checks-readability-dry-refactor-needs">3. Perform code-quality checks (readability, DRY, refactor needs).</h3>
<p>AI often produces verbose or duplicated logic. Ensure names are meaningful, side effects are clearly stated, and duplication is removed or minimized. Run linters/formatters, collapse repetition, and extract helpers where they aid clarity.</p>
<h3 id="heading-4-analyze-organization-and-structure-make-sure-it-fits-the-architecture">4. Analyze organization and structure (make sure it fits the architecture).</h3>
<p>AI writes code in isolation. Confirm the change uses existing utilities, layers, and boundaries (domain/services/controllers/jobs). Check imports and module placement, avoid reinventing existing helpers, and align with repository conventions.</p>
<h3 id="heading-5-validate-inputs-and-assumptions-make-the-implicit-explicit">5. Validate inputs and assumptions (make the implicit explicit).</h3>
<p>List the assumptions the AI made (default locale/timezone, allowed ranges, required fields). Add schema validation (DTO/class validators/JSON Schema). Empty, null, over-max, non-ASCII, unexpected enum, malicious strings. And finally, enforce limits/timeouts.</p>
<h3 id="heading-6-perform-security-audits-minimum-pass">6. Perform security audits (minimum pass).</h3>
<ul>
<li><p><strong>AuthN/AuthZ:</strong> Confirm endpoint checks identity and authorization paths; deny-by-default.</p>
</li>
<li><p><strong>Inputs:</strong> Sanitize/validate inputs, prevent injection (SQL/NoSQL/command), and escape user-visible output.</p>
</li>
<li><p><strong>Secrets</strong>: No secrets in code/diff/logs, use env/secret manager, and rotate any test keys.</p>
</li>
<li><p><strong>Abuse controls:</strong> Add rate limits, size limits, and timeouts on network and disk operations. Run SAST/secret scan/SCA, and fix or justify findings.</p>
</li>
</ul>
<h3 id="heading-7-do-a-performance-evaluation-right-now-at-a-small-scale">7. Do a performance evaluation (right now, at a small scale).</h3>
<p>Look for N+1s, needless network calls, unbounded loops, quadratic sorts. Add a micro-benchmark or run a quick load test for hot paths. Set sensible cache/timeout/retry with jitter where applicable.</p>
<h3 id="heading-8-manage-dependencies-pin-justify-minimize">8. Manage dependencies (pin, justify, minimize).</h3>
<p>Review any new libraries. Are they necessary? Maintained? License compatible? Pin versions, add lockfiles, or remove unused transitive adds.</p>
<h3 id="heading-9-review-documentation-what-to-add-and-where">9. Review documentation (what to add and where).</h3>
<p>Ensure the docs are in line with the code. AI often changes some parts or adds code blocks at different places while resolving various issues. These changes might not make it into the docs.</p>
<h3 id="heading-10-observability-see-problems-early">10. Observability (see problems early).</h3>
<p>Use structured logs with request/trace IDs, key counters/timers (success/error/latency), health/readiness probes, and a basic dashboard or alert stub.</p>
<h3 id="heading-11-compliance-and-data-handling-when-applicable">11. Compliance and data handling (when applicable).</h3>
<p>Identify any personally identifiable information (PII), document collection/retention, ensure masking/redaction in logs, verify dependency licenses and data-residency constraints.</p>
<h2 id="heading-how-to-work-effectively-with-ai-tools">How to Work Effectively with AI Tools</h2>
<p>At this point, you can probably see why it’s very important to understand the actual skills involved in AI-assisted development.</p>
<p>There’s a pretty big difference between an experienced developer who uses AI tools to help them get more done, and a newbie who thinks AI can build the next Facebook or Google just with a simple prompt.</p>
<p>An inexperienced dev will ask AI something like "Hey, Build me Twitter and make no mistakes"</p>
<p>But an experienced developer who has a solid fundamentals might say say something like:</p>
<ul>
<li><p>"AI, we're building a Twitter replica. Use $SQL_Database, Use $Language, Avoid $Common_Pitfalls, Follow $Standard_Practices."</p>
</li>
<li><p>"The generated code is prone to X problem, implement this fix."</p>
</li>
<li><p>"Implementation of $X is flawed because of $Y, do $Z instead."</p>
</li>
</ul>
<p>So as you can see, you still need to know the how's and the why's and what depends on what. Often you’ll just need to make the changes manually, because it will be faster. And you don’t want to outsource the critical thinking part, which is the part that AI can't actually do.</p>
<p>LLMs are good at information retrieval. If you know nothing about what you’re looking for, then asking an AI isn’t going to be that helpful (or that reliable). But if you have an idea, some background knowledge/context, and the skills to verify AI’s responses, then it can be really helpful.</p>
<p>Last month, I shared in my <a target="_blank" href="https://bytesizedbets.com/">newsletter</a> how my current coding loop looks in practice.</p>
<p>I draft with Claude Code (or Copilot/Cursor), open a PR, and let an AI reviewer like CodeRabbit (or Copilot PR Reviewer / Cursor Bugbot or Greptile) do the first pass. CI runs tests and scans.</p>
<p>I repeat until everything’s green and the PR is ready to merge. It’s fast, but it’s still disciplined.</p>
<p>If you want to understand why this kind of workflow is becoming essential, read this article: <a target="_blank" href="https://bytesizedbets.com/p/era-of-ai-slop-cleanup-has-begun">Era of AI Slop Cleanup Has Begun</a>. I talk about what’s happening in AI-assisted engineering, where generating code is easy, but keeping it clean and production ready takes experience – and you must have good programming skills.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>AI-generated code can boost productivity – but production value still comes from software that is robust, secure, and maintainable.</p>
<p>Mindless code generation creates technical debt. But when you integrate AI thoughtfully, with guardrails, verification, tests, security checks, and documentation, you can go faster without lowering your standards.</p>
<p>That's it for this article. I hope you learned something new today.</p>
<p>If you have any questions about code reviews, engineering, startups, or business in general, please find me on Twitter: <a target="_blank" href="https://x.com/TheAnkurTyagi">@TheAnkurTyag</a>i. I’d be more than happy to discuss them.</p>
<h3 id="heading-want-to-read-more-interesting-articles-like-this">Want to read more interesting articles like this?</h3>
<p>You can read more about the latest dev tools like this one on my <a target="_blank" href="https://www.devtoolsacademy.com/">website</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Refactor Complex Codebases – A Practical Guide for Devs ]]>
                </title>
                <description>
                    <![CDATA[ Developers often see refactoring as a secondary concern that they can delay indefinitely because it doesn’t immediately contribute to revenue or feature development. And managers frequently view refactoring as "not a business need" until it boils ove... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-refactor-complex-codebases/</link>
                <guid isPermaLink="false">682df5a0f2057ab279952dbe</guid>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webdev ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ code review ]]>
                    </category>
                
                    <category>
                        <![CDATA[ refactoring ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ankur Tyagi ]]>
                </dc:creator>
                <pubDate>Wed, 21 May 2025 15:47:44 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747835131515/f6ea465a-9b14-4918-8943-87ec225b19b3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Developers often see refactoring as a secondary concern that they can delay indefinitely because it doesn’t immediately contribute to revenue or feature development.</p>
<p>And managers frequently view refactoring as "not a business need" until it boils over and becomes the most significant business need possible.</p>
<blockquote>
<p><em>"Oh, our software somehow works. We can't implement any new changes. And oh, everyone is quitting because work is miserable."</em></p>
</blockquote>
<p>In this article, I’ll walk you through the steps I use to refactor a complex codebase. We’ll talk about setting goals, writing tests, breaking up monoliths into smaller modules, verifying changes, making sure existing features still work, and keeping tabs on performance. I’ll also show you how to speed up reviews using AI tools.</p>
<p>By following these steps, you can turn complex, fragile code into a clean, reliable codebase your team can own.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXccvZ3sehF8oGifjnapnY9AUcPde9aKy9t_YEUeL8M2s3dcwxFq_bJLCSp_S02fIvfbwzpZfkz7e-2JQpXpzcdqELqs80EjkLLRpz0Uat6q9_RcRM5VQbjLoUxA2GlaqyeolsKGeA?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="code-refactoring" width="600" height="400" loading="lazy"></p>
<h2 id="heading-the-issue-of-technical-debt">The Issue of Technical Debt</h2>
<p>As projects grow and evolve, <a target="_blank" href="https://en.wikipedia.org/wiki/Technical_debt">technical debt</a> increases. Code that was once functional and manageable turns into an unmaintainable mess, where even small changes become risky and time-consuming.</p>
<p>Despite the obvious need for cleanup, refactoring rarely gets prioritized because there's always something more urgent, new features, bug fixes, and client demands.</p>
<p>I’ve had conversations with engineers, many of whom are working on enterprise software and are fully aware of their codebase's code smells and inconsistencies. They dislike the situation but feel powerless to change it.</p>
<p>So how do we shift from a culture of writing for pure functionality to a culture that values maintainability, especially for complex codebases?</p>
<p>It’s usually a mistake to completely halt new feature development for a long refactoring period (except perhaps in emergencies). Business needs still exist, and putting everything on hold can create tension and lost opportunities. It’s better to find a balance so you’re still delivering value to users even as you clean under the hood.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXeZx-XKCA2DC6kQQe2-4NU07wKEm0_VZ4kqEjbF6u2vy2paRigdNRUGjr-_AoE6ueNjCxNjnB-mI7uroXFhJ0nFfvWzwYq2VUMsdsPhXu4KvGYSZcUN0nFmKg8U8WzgGJQAgKtUaw?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="Uncle-bob-take-on-refactoring" width="600" height="400" loading="lazy"></p>
<p>While there is no one-size-fits-all solution, a structured approach can help teams introduce sustainable refactoring practices, even in environments where management is resistant. Let’s explore how this works.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents:</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-refactoring">What is Refactoring?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-preparing-for-refactoring">Preparing for Refactoring</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-secure-management-buy-in">Secure Management Buy-in</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-ensure-a-safety-net-with-automated-testing">Ensure a Safety Net with Automated Testing</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-identify-high-risk-areas">Identify High-Risk Areas</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-set-clear-refactoring-goals">Set Clear Refactoring Goals</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-techniques-for-refactoring-complex-codebases">Techniques for Refactoring Complex Codebases</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-1-identifying-and-isolating-problem-areas">1. Identifying and Isolating Problem Areas</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-2-incremental-vs-big-bang-refactoring">2. Incremental vs. Big Bang Refactoring</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-3-breaking-down-monolithic-code">3. Breaking Down Monolithic Code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-4-ensuring-backward-compatibility">4. Ensuring Backward Compatibility</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-5-handling-dependencies-and-tight-coupling">5. Handling Dependencies and Tight Coupling</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-6-testing-strategiessafely-refactoring-with-confidence">6. Testing Strategies (Safely Refactoring with Confidence)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-7-refactoring-without-breaking-performance">7. Refactoring Without Breaking Performance</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-8-automate-code-reviews-with-ai-tools">8. Automate Code Reviews with AI Tools</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-summary">Summary</a></p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-what-is-refactoring"><strong>What is Refactoring?</strong></h2>
<p>Many people all too often use the word "refactor" when they mean a targeted rewrite.</p>
<p>As Martin Fowler famously said,</p>
<blockquote>
<p><em>“Refactoring is a controlled technique for improving the design of an existing code base. Its essence is applying a series of small behavior-preserving transformations... However, the cumulative effect... is quite significant.”</em>​</p>
</blockquote>
<p>In practice, this means continuously polishing code to reduce complexity and technical debt.</p>
<p>While traditional software development follows a linear approach of designing first and coding second, real-world projects often evolve in ways that lead to structural decay. Refactoring counteracts this by continuously refining the codebase, transforming disorganized or inefficient implementations into well-structured, maintainable solutions.</p>
<p>A targeted rewrite is a focused overhaul of a specific aspect of an application, often affecting multiple parts of the codebase. It carries more risk than refactoring but is still controlled and contained.</p>
<h2 id="heading-preparing-for-refactoring">Preparing for Refactoring</h2>
<p>Even the most skilled refactoring effort can stall without proper preparation. Before you start moving code around, laying a foundation that will keep your work organized and your team on the same page is crucial.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXcr3hNpzC9XPUVnG6d7uHuC977aYrG2VVOH-8E4WhzM5Rfz3vzPDUPTwJChrK0l7WUK8BLTzYr5-295_27ARWQvcmjufXOk68Bg8szUjEq3IFVCDO0XfTSRFy1LaxqyjvjVDNddsw?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="martin-fowler-on-refactoring" width="600" height="400" loading="lazy"></p>
<p>Here are some steps you can take to ensure your refactoring efforts are successful.</p>
<h3 id="heading-secure-management-buy-in">Secure Management Buy-in</h3>
<p>As I’ve already discussed, getting time for refactoring can be difficult in feature-driven organizations. Often, management will accept refactoring investment if you can tie it to business outcomes, faster time to market, fewer outages (which translates to happier customers), and the ability to take on new initiatives.</p>
<p>Make those connections explicit. For example, you could say:</p>
<blockquote>
<p><em>“If we refactor our reporting engine now, it will make it feasible to add the analytics module next quarter, which unlocks a new revenue stream.”</em></p>
</blockquote>
<p>Or use data:</p>
<blockquote>
<p><em>“We spent 30% of our last sprint fixing bugs in module Y. After refactoring Y, we expect that to drop significantly, freeing time for new features.”</em></p>
</blockquote>
<p>Business-minded arguments help justify the balance.</p>
<h3 id="heading-ensure-a-safety-net-with-automated-testing">Ensure a Safety Net with Automated Testing</h3>
<p>As you refactor, tests are your safety net. Before modifying a component, write characterization tests around it if they don’t exist.</p>
<pre><code class="lang-python"><span class="hljs-comment"># example: characterization test for a legacy function</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">legacy_calculate_discount</span>(<span class="hljs-params">price, rate</span>):</span>
    <span class="hljs-comment"># ... complex logic you don't fully understand yet ...</span>
    <span class="hljs-keyword">return</span> price * (<span class="hljs-number">1</span> - rate/<span class="hljs-number">100</span>) <span class="hljs-keyword">if</span> rate &lt; <span class="hljs-number">100</span> <span class="hljs-keyword">else</span> <span class="hljs-number">0</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">test_legacy_calculate_discount</span>():</span>
    <span class="hljs-comment"># capture existing behavior</span>
    <span class="hljs-keyword">assert</span> legacy_calculate_discount(<span class="hljs-number">100</span>, <span class="hljs-number">10</span>) == <span class="hljs-number">90</span>
    <span class="hljs-keyword">assert</span> legacy_calculate_discount(<span class="hljs-number">50</span>, <span class="hljs-number">200</span>) == <span class="hljs-number">0</span>
</code></pre>
<p>These tests capture the current behavior, so you’ll know if you accidentally change it. Unit tests, integration tests, and e2e tests all validate that refactoring hasn’t broken anything.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfWfke-9FxoQIPFwRWVoIWrYN7L40mEmhpdAUkcBm34mwzXJ0R8jXKH8rZ0HjAghAtQ-v6dTUYYvK0T8_QBgyfeab-7R50pnB6BgdDm9L4PkFwvwGlUYTHNo21f37fxMZYt3xeY?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="automated-testing-is-imp-for-refactoring" width="600" height="400" loading="lazy"></p>
<p>It’s often worth investing time in setting up a continuous integration pipeline so that every change triggers automated tests. This gives rapid feedback and confidence that you’re not introducing regressions. Robust testing and CI/CD enable you to move faster and refactor with peace of mind.</p>
<pre><code class="lang-powershell"><span class="hljs-comment"># .github/workflows/ci.yml</span>
name: CI
on: [<span class="hljs-type">push</span>, <span class="hljs-type">pull_request</span>]
jobs:
  test:
    runs<span class="hljs-literal">-on</span>: ubuntu<span class="hljs-literal">-latest</span>
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup<span class="hljs-literal">-python</span>@v4
        with: python<span class="hljs-literal">-version</span>: <span class="hljs-string">'3.10'</span>
      - run: pip install <span class="hljs-literal">-r</span> requirements.txt
      - run: pytest -<span class="hljs-literal">-maxfail</span>=<span class="hljs-number">1</span> -<span class="hljs-literal">-disable</span><span class="hljs-literal">-warnings</span> <span class="hljs-literal">-q</span>
</code></pre>
<h3 id="heading-identify-high-risk-areas">Identify High-Risk Areas</h3>
<p>The first step is to figure out what to refactor. High-risk areas are parts of the code likely to cause bugs or slow development. Common signs include long methods, large classes, duplicate code, and complex conditional logic​.</p>
<p>Such code “smells” often hint at deeper design problems. Tools like static analysis can automatically flag these issues.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfS4aFy2hyRSq3UmgB2gQ8NN_-yUksNXcSavTtpnL8KIiWpGGidCSCstLKANZGOjJLqEF69wp-xjMGH6jrjurSaFtUIMS09vUaDgJ6vGtyabP-4QC5ISmT_cMvaaw6c2KlyVa1CKQ?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="SonarQube-dashboard" width="600" height="400" loading="lazy"></p>
<p>For example, SonarQube will mark code smells (like high complexity or long methods) that increase technical debt​. Using SonarQube or similar tools, you can generate reports on code complexity (for example, cyclomatic complexity metrics​) and find hotspots in the codebase that need more attention.</p>
<h3 id="heading-set-clear-refactoring-goals">Set Clear Refactoring Goals</h3>
<p>Before refactoring code, define the goal.</p>
<p>Goals must be specific and measurable. For example, you might aim to reduce a class’s size or a function’s <a target="_blank" href="https://www.ibm.com/docs/en/raa/6.1.0?topic=metrics-cyclomatic-complexity">cyclomatic complexity</a> by a certain amount or to increase unit test coverage from 60% to 90%.</p>
<p>Each goal is tied to a measurable outcome: shorter methods, fewer if statements or classes with a single responsibility, faster execution for processing orders, higher test coverage, and no unused code. These targets will guide our refactoring plan and let us verify when we’ve succeeded.</p>
<p><strong>Tip:</strong> Write down your refactoring goals and share them with your team. This sets expectations that you’re not adding new features in this effort, just making the code cleaner and more robust. It also helps justify the time spent by showing the benefits (like more straightforward future additions and fewer bugs).</p>
<h2 id="heading-techniques-for-refactoring-complex-codebases">Techniques for Refactoring Complex Codebases</h2>
<h3 id="heading-1-identifying-and-isolating-problem-areas">1. Identifying and Isolating Problem Areas</h3>
<p>It can be overwhelming to decide where to start refactoring a large codebase. Not every part of the code needs refactoring – some areas are delicate or rarely touched.</p>
<p>The most impactful refactoring efforts typically target the “problem areas”: parts of the codebase that are overly complex, error-prone, or act as bottlenecks for development and performance. Identifying these areas is a crucial first step.</p>
<h3 id="heading-techniques-for-finding-hotspots">Techniques for Finding Hotspots</h3>
<h4 id="heading-team-knowledge-amp-developer-frustration">Team knowledge &amp; developer frustration</h4>
<p>Don’t underestimate the value of anecdotal information from the team. Which parts of the code do developers dread working in? Often, the team’s instincts point to areas that are hard to understand or modify (for example, “the accounting module is a black box, we hate touching it”). These could be areas to improve.</p>
<p>In my experience, simply asking, “If you had a magic wand, which part of the code would you rewrite?” yields very insightful answers.</p>
<h4 id="heading-code-complexity-metrics">Code complexity metrics</h4>
<p>Use static analysis tools to measure cyclomatic complexity, code duplication, large functions/classes, and so on. Files or modules with extremely high complexity numbers or thousands of lines are good candidates for scrutiny. But static complexity alone doesn’t tell the whole story – a file might be ugly but rarely touched.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXc07SWwlu4GxU6AwoXQEHyyEcQY-6YMOEPr7b7Quhk5UvLD7qx9XyZla2SzP32eGFoYY_Xy-SYZQ9mOMX7Mxeq1YCnFXQxudsMNbvak9CLZfSOeRIvdll_pLW56sAmvRcPZMk36Rg?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="SonarQube" width="600" height="400" loading="lazy"></p>
<h4 id="heading-change-frequency-churn">Change frequency (Churn)</h4>
<p>Look at version control history to see which files are often changed, especially those associated with bug fixes or incidents.</p>
<h4 id="heading-hotspot-analysis">Hotspot analysis</h4>
<p>A robust approach combines complexity and change frequency to find “hotspots.” For example, a tool or technique plotting modules by their complexity and how often they change can highlight the problematic areas. CodeScene (a code analysis tool) popularized this: <em>hotspots</em> are parts of the code that are highly complex and frequently modified, indicating areas where “paying down debt has a real impact”​.</p>
<p>If a module is a mess and developers are in it every week, improving that module will likely yield outsized benefits (fewer bugs, faster adds).</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXdJkGfbDK6UFDN9hqzeyCMBWmajADhAMJwzSouyMNz_63o9SRNfOly9AP_XiY2jqfi02fHSIFkMBCfstkjJfkxVB-NaHCSit0xssTYfztZ2BRQZmqYr_lTc3R750-1-lrJi7eeViQ?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="code-health-dashboard" width="600" height="400" loading="lazy"></p>
<h4 id="heading-performance-bottlenecks-and-crashes">Performance bottlenecks and crashes</h4>
<p>Some parts of the codebase become targets for refactoring because they cause frequent performance problems or outages. For instance, if a specific service or job crashes often or can’t keep up with the load, you might need to refactor it for stability.</p>
<h3 id="heading-how-to-isolate-problem-areas">How to Isolate Problem Areas</h3>
<p>Once you’ve identified a hotspot or problem area, the next challenge is isolating it so you can refactor safely. In a complex system, nothing lives in complete isolation. That problematic module likely interacts with many others.</p>
<p>Here are strategies to isolate and tackle it:</p>
<h4 id="heading-break-dependencies-create-seams">Break dependencies (Create seams)</h4>
<p>Michael Feathers (in <em>Working Effectively with Legacy Code</em>) introduced the concept of “seams” – places where you can cut into a codebase to isolate a part for testing or refactoring. This might mean introducing an interface or abstraction between components so you can work on one side independently.  </p>
<p>For example, suppose PaymentService is tightly coupled to StripeGateway, with direct calls scattered throughout the code.</p>
<pre><code class="lang-python"><span class="hljs-comment"># payment_service.py</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">charge_customer</span>(<span class="hljs-params">order_id, amount</span>):</span>
    <span class="hljs-comment"># Hardcoded dependency to Stripe</span>
    stripe = StripeGateway()
    stripe.charge(order_id, amount)
</code></pre>
<p>To isolate and refactor the payment logic safely, you can introduce a <code>PaymentProcessor</code> interface and have <code>PaymentService</code> depend on that interface instead. Then, create an adapter like StripeAdapter that implements PaymentProcessor and delegates to the existing Stripe logic.</p>
<p>This way, you can safely refactor or even replace the Stripe integration behind the StripeAdapter without impacting <code>PaymentService</code> or any other module that uses it. As long as the <code>PaymentProcessor</code> interface is honored, the rest of the system remains unaffected.</p>
<pre><code class="lang-python"><span class="hljs-comment"># interfaces.py</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentProcessor</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">charge</span>(<span class="hljs-params">self, order_id, amount</span>):</span>
        <span class="hljs-keyword">raise</span> NotImplementedError


<span class="hljs-comment"># stripe_adapter.py</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StripeAdapter</span>(<span class="hljs-params">PaymentProcessor</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">charge</span>(<span class="hljs-params">self, order_id, amount</span>):</span>
        <span class="hljs-comment"># Internally still uses Stripe</span>
        stripe = StripeGateway()
        stripe.charge(order_id, amount)


<span class="hljs-comment"># payment_service.py (Refactored)</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentService</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, processor: PaymentProcessor</span>):</span>
        self.processor = processor

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">charge_customer</span>(<span class="hljs-params">self, order_id, amount</span>):</span>
        self.processor.charge(order_id, amount)
</code></pre>
<h4 id="heading-branch-by-abstraction">“Branch-by-abstraction”</h4>
<p>This technique is related to the above and is often used in continuous delivery. The idea is to add a layer of abstraction (like an interface or proxy) in front of the old code, have both old and new code implementations behind it, and then gradually shift usage from the old to the new implementation. For a while, you might have a temporary state where both versions exist (perhaps toggled by a config or feature flag).</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXcaFoXSHVYTBz_1DOsucPkvwGQwfo9qrvhPYvvjYOXQsLIh2MCTfseB1g9SOfijpdKMwcwmK4lfPWcyhn4vf5gaFwdliKUZUGDOcQVJ0qupRLjvnhFrSm5LZfe8OoqZtZkHkj9IXw?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="Branch-by-abstraction" width="600" height="400" loading="lazy"></p>
<p>This is similar to how the strangler fig pattern works at an architectural level. It’s a bit of extra work (since you maintain two paths for a while), but it allows you to migrate functionality and fall back if needed incrementally.</p>
<p>Aim to identify the 20% of the code causing 80% of the problems. Focus your refactoring energy there for maximum impact. When you do, create a plan to isolate that area via abstractions, interfaces, modules, or other means so that you can work on it with minimal risk of side effects. The more you can contain the blast radius of a refactoring, the more confidently you can move forward.</p>
<h3 id="heading-2-incremental-vs-big-bang-refactoring">2. Incremental vs. Big Bang Refactoring</h3>
<p>One of the first strategic decisions is approaching the refactor <strong>incrementally</strong> or going for a <strong>“big bang”</strong> overhaul. In most cases, an incremental approach is preferable, but there are scenarios where more significant coordinated refactoring steps are considered.</p>
<p><strong>Let’s break down what these mean:</strong></p>
<pre><code class="lang-python"><span class="hljs-comment"># before: one large function with multiple responsibilities</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_order</span>(<span class="hljs-params">order</span>):</span>
    validate(order)
    apply_discount(order)
    save_to_db(order)
    send_confirmation(order)
    log_metrics(order)
    update_loyalty_points(order)
    <span class="hljs-comment"># potentially more steps </span>

<span class="hljs-comment"># after: refactored incrementally into clearer, smaller units</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_order</span>(<span class="hljs-params">order</span>):</span>
    validate(order)
    apply_discount(order)
    persist_and_notify(order)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">persist_and_notify</span>(<span class="hljs-params">order</span>):</span>
    save_to_db(order)
    send_confirmation(order)
    log_metrics(order)
    update_loyalty_points(order)
</code></pre>
<h4 id="heading-incremental-refactoring">Incremental refactoring</h4>
<p>This means making small, manageable changes over time rather than attempting a massive overhaul in one shot. The system should remain functional at each step (even internally in transition). The advantage is risk mitigation: each small change is less likely to go wrong, and it’s easier to pinpoint and fix if it does.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXdaSmnIWRE9FKNmmABBzc6Tk6KFwsj29FQ2YwyQ_kWqryheb0yUdpec51lQHg5XahoxKgCm4vv9twD849H3Yo5dn0678tuGih9Z-HfBBCfhBngs4YhpH6x2pjzqnAeDVYGohXHvDQ?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="Incremental refactoring" width="600" height="400" loading="lazy"></p>
<p>Incremental delivery lets you confirm changes in production and makes diagnosing issues easier since you’re only changing one small thing at a time​. It also means the system keeps running during the refactor, so there’s less pressure to rush to “get the system back to working condition”​. If priorities shift, you can pause after some increments and still have a working product.</p>
<h4 id="heading-big-bang-refactoring-rewrite">Big bang refactoring (Rewrite)</h4>
<p>This is the “tear it down and rebuild” approach. You stop adding new features, possibly freeze the code for a period, and devote a considerable effort to redesigning or rewriting a significant portion (or the entirety) of the system. The idea is to emerge on the other side with a <em>brand new, clean</em> system.</p>
<p>So when (if ever) is a big bang justified? Perhaps when the existing system is truly untenable – for example, an outdated technology that <strong>must</strong> be replaced (such as a platform that can’t meet new performance or security requirements or code written in a language no longer supported). Even then, wise teams often simulate a big bang by breaking it into stages or developing the new system in parallel.</p>
<p>Whenever possible, favor an incremental refactoring strategy. Teams successfully pull off massive transformations by treating the big refactor as a series of mini-refactors under a shared vision.</p>
<h3 id="heading-3-breaking-down-monolithic-code">3. Breaking Down Monolithic Code</h3>
<p>Many complex codebases start life as a single monolithic application, one deployable, a single code project, or a tightly coupled set of modules all maintained and released together.</p>
<p>Over time, monoliths can become unwieldy, builds take forever, a change in one area can unintentionally affect another, and teams can be complex to scale because everyone is stepping on each other’s toes in the same code. A common refactoring challenge for senior engineers is modularising or splitting a monolith into more manageable pieces.</p>
<pre><code class="lang-python"><span class="hljs-comment"># define the interface</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentProcessor</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">charge</span>(<span class="hljs-params">self, amount</span>):</span> ...

<span class="hljs-comment"># old implementation</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LegacyProcessor</span>(<span class="hljs-params">PaymentProcessor</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">charge</span>(<span class="hljs-params">self, amount</span>):</span>
        <span class="hljs-comment"># original code</span>

<span class="hljs-comment"># new implementation behind a feature flag</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NewProcessor</span>(<span class="hljs-params">PaymentProcessor</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">charge</span>(<span class="hljs-params">self, amount</span>):</span>
        <span class="hljs-comment"># cleaner code</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_processor</span>():</span>
    <span class="hljs-keyword">if</span> config.feature_new_payment:
        <span class="hljs-keyword">return</span> NewProcessor()
    <span class="hljs-keyword">return</span> LegacyProcessor()

<span class="hljs-comment"># usage remains the same</span>
processor = get_processor()
processor.charge(<span class="hljs-number">100</span>)
</code></pre>
<h4 id="heading-strategies-for-modularization">Strategies for modularization.</h4>
<ul>
<li><p><strong>Layer separation:</strong> Start by enforcing logical layer boundaries. For example, separate the user interface code from business logic and separate business logic from data access. In a messy monolith, these concerns often get mixed together. By organizing the code into layers (even within the same repository), you can limit the ripple effect of changes.</p>
</li>
<li><p><strong>Domain-based modularization:</strong> If your system spans multiple business domains or functional areas, consider splitting along those lines. For example, an e-commerce monolith might be separated into modules like Accounts, Orders, Products, Shipping, and so on.<br>  Each could become a subsystem or a package. The goal is to minimize the information these modules need to know about each other’s internals (high cohesion within modules and clear APIs between them).</p>
</li>
<li><p><strong>Microservices or services extraction:</strong> In recent years, the trend has been to break monoliths into microservices, independent services that communicate over APIs. This form of architectural refactoring can significantly improve independent deployability and scalability. But it’s a significant undertaking with complexities (distributed systems, network calls, and so on). If you decide to go this route, do it gradually.<br>  A proven method is the <strong>strangler fig pattern</strong> mentioned earlier: you pick one piece of functionality and rewrite or extract it as a separate service, redirect traffic or calls to the new service. At the same time, the rest of the monolith remains intact and iteratively does this for other pieces​.</p>
</li>
<li><p><strong>Modular monolith:</strong> Not every system needs to go full microservices. There’s an approach called a modular monolith, essentially structuring your single application into well-defined modules that communicate via explicit interfaces (almost like internal microservices but without the overhead of separate deployments).</p>
</li>
</ul>
<p>This can give you many microservices' advantages (clear boundaries, separate development responsibility) while avoiding operational complexity.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfLiNAEDyOsR4G_q1oQS3jpSenci3XDJRm10Gy3picTpaO9uHwme2H3YkbJF-Jrvqq3Q-QMxGjJJwy04mqUf1a7D8IRsCDER5pHBT6GTMPRkao5EXXIFGtj4Iki15mOHmRKRLTiWw?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="microservices' advantages" width="600" height="400" loading="lazy"></p>
<ul>
<li><strong>Identify shared utilities vs. truly independent components:</strong> In breaking down a monolith, some code is widely shared (like utility functions or cross-cutting concerns such as authentication). It might make sense to factor those into libraries or services <em>first</em>, as they will be needed by whatever other pieces you split out.</li>
</ul>
<p>While breaking down a monolith, maintaining functionality during the transition is essential. Techniques like backward compatibility (discussed next) and thorough testing will be your safety net.</p>
<p>Finally, be prepared for the team workflow to change. If you move to microservices, teams might take ownership of different services, requiring more DevOps and communication across teams. If you keep a modular monolith, enforce code ownership or review rules to keep the modules from tangling up again (for example, you might restrict direct database access from one module to another’s tables, and so on).</p>
<h3 id="heading-4-ensuring-backward-compatibility">4. Ensuring Backward Compatibility</h3>
<p>A critical concern during large refactoring is: <em>Will our changes break existing contracts</em>?</p>
<p>In other words, can other systems, modules, or clients that rely on our code work as expected after we refactor? Backward compatibility is especially important if your codebase provides public APIs (to external customers or other teams), data persisted in a certain format, configuration files that users have written, etc.</p>
<p>Here are some strategies and considerations to maintain backward compatibility:</p>
<p>Suppose you have a widely-used function like <code>send_email(to, subject, body)</code>. You want to refactor the internal logic to support additional features like HTML formatting, but you don’t want to break existing callers.</p>
<p>Instead of changing the function signature, you keep the public API unchanged and delegate to a new internal function:</p>
<pre><code class="lang-python"><span class="hljs-comment"># original API</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_email</span>(<span class="hljs-params">to, subject, body</span>):</span>
    <span class="hljs-comment"># send mail...</span>

<span class="hljs-comment"># refactored internals, keep signature</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_email</span>(<span class="hljs-params">to, subject, body</span>):</span>
    sendv2(to=to, subject=subject, body=body)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">sendv2</span>(<span class="hljs-params">to, subject, body, html=True</span>):</span>
    <span class="hljs-comment"># new implementation with HTML support</span>
</code></pre>
<p>The internal <code>send_email_v2()</code> function adds new capabilities like HTML formatting, but older code using <code>send_email()</code> still works without any modifications.</p>
<p>If you're introducing a new, improved version like <code>send_email_v2(to, subject, body, html=True)</code>, it's good practice to:</p>
<ul>
<li><p>Mark the old version (send_email) as deprecated in documentation.</p>
</li>
<li><p>Ensure the old version internally calls the new one.</p>
</li>
<li><p>Give other teams time to migrate at their own pace.</p>
</li>
</ul>
<h4 id="heading-use-versioning-for-external-apis">Use versioning for external APIs</h4>
<p>If your system provides an HTTP API or similar to external clients, the safest route for major changes is to version the API. Introduce a v2 API endpoint for the refactored logic, keep v1 running (maybe internally calling v2 or using a translation layer). Clients can move to v2 at their own pace.</p>
<p>It’s extra work to maintain two APIs temporarily, but it prevents a breaking change from angering users or causing outages. Always communicate changes clearly and provide migration guides if applicable.</p>
<h4 id="heading-have-a-clear-deprecation-policy">Have a clear deprecation policy</h4>
<p>Make sure there’s a policy (and communication) around how long deprecated features will be supported. For internal APIs, maybe it’s one release cycle. For external ones, maybe multiple cycles or never removal without a major version bump. A good practice is to announce deprecation early.</p>
<p>If you’re exposing an HTTP API, consider introducing a new versioned endpoint (for example, <strong>/api/v2/send_email</strong>) and maintain the older <strong>/api/v1/send_email temporarily</strong>. Internally, v1 might call v2 with default parameters, ensuring behavior stays consistent for existing clients.</p>
<p>In summary, maintain backward compatibility whenever possible, and implement a clear deprecation policy for anything you do change​.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXe3xM4som_GQrtHXI3NNR0G-4KJ-1D2YO-JbNdT75IxZ5_upcBRDnOVp7krEESiqwwtXg18pDypLq3VxDr44Hof76cs8HajOZy2w0FZ50kWmPk6Y7EwNByNLNrqAokmhmmL5sP3AA?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="Clear deprecation policy" width="600" height="400" loading="lazy"></p>
<h4 id="heading-write-adapter-or-compatibility-layers">Write adapter or compatibility layers</h4>
<p>In some cases, you can write an adapter to bridge old and new systems. For instance, suppose you refactor the underlying data model of your application, but you still have old configuration files in the old format. Rather than forcing all those files to be rewritten immediately, you could write a small adapter that translates the old format to the new one at runtime (or during startup). This way, old data continues to work. </p>
<h4 id="heading-test-for-compatibility">Test for compatibility</h4>
<p>Include tests that specifically ensure backward compatibility. For instance, if you have a public API, keep a suite of tests using the old API contracts and run them against the refactored code, they should still pass. </p>
<p>In summary, ensure that as you refactor, the external behavior and contracts remain consistent. This careful approach protects your users and downstream systems, allowing you to reap the internal benefits of refactoring without causing external chaos.</p>
<h3 id="heading-5-handling-dependencies-and-tight-coupling">5. Handling dependencies and tight coupling</h3>
<p>One of the hairiest aspects of refactoring a large codebase is dealing with deeply interdependent code. Complex systems often suffer from tight coupling. Module A assumes details about Module B and vice versa, global variables or singletons are used all over, or a change in one place ripples through half the codebase.</p>
<p>Reducing coupling is a significant aim of refactoring because it makes the code more modular, meaning each piece can be understood, tested, and changed independently. So, how do we gradually loosen the coupling in a legacy system?</p>
<p>Let’s go over some strategies to reduce coupling.</p>
<h4 id="heading-introduce-interfaces-or-abstraction-layers">Introduce interfaces or abstraction layers</h4>
<p>A very effective way to decouple is to put an interface between components. For example, if you have a class that directly queries a database, introduce an interface and have the class use that instead. The underlying database code implements the interface.</p>
<pre><code class="lang-python"><span class="hljs-comment"># before: direct instantiation</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrderService</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.repo = OrderRepository()

<span class="hljs-comment"># after: inject dependency</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrderService</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, repo</span>):</span>
        self.repo = repo

<span class="hljs-comment"># wiring up in application startup</span>
repo = OrderRepository(db_conn)
service = OrderService(repo)
</code></pre>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfuMNvzC4x3X0EOgoRXzflfOv4C-Dxzc2Tm16KA0NdZcOH0nK300LUwcNzXCL6iqu0rhknHiVhnQN4csDCYUupQLc4Kt6Q4c7d1Pi47NfrXKoF9rhXCUMAhtozsDpFMVT2lo2OX5Q?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="Introduce interfaces or abstraction layers" width="600" height="400" loading="lazy"></p>
<p>Now, that class no longer depends on how the data is fetched. Applying the dependency inversion principle depends on abstractions, not concretions.</p>
<h4 id="heading-use-dependency-injection">Use dependency injection</h4>
<p>Once you have interfaces, use dependency injection to supply concrete implementations. Many frameworks support DI containers, or you can do it manually (passing in dependencies via constructors). Dependency injection means code A doesn’t instantiate code B itself – instead, B is passed into A.  </p>
<p>This approach also makes unit testing easier (you can inject mock dependencies).</p>
<h4 id="heading-facades-or-wrapper-services">Facades or wrapper services</h4>
<p>If a particular subsystem is heavily entangled with others, consider creating a Facade, an object that provides a simplified interface to a larger body of code. Other parts of the system are then called the Facade, not the many internal methods of the subsystem. Internally, the subsystem can be refactored (even split into smaller pieces) as long as the Facade’s outward interface remains consistent.</p>
<p>This is similar to how microservices work (other services don’t care how one service is implemented internally – they just call its API), but you can do it in-process, too.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXe_X2G_VNTR-I2EIp86SgPD3Zlks70Q4iG3BsqIs94PMgh-_qNfRk7ogT4mqONP7qXzg8PpN92k342-2nH6ertfy32Ga6SFH3PdSLwxP4US9PPjMi6Rqc9hy-gHbSKVzvTvYmTzOQ?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="Facades or wrapper services" width="600" height="400" loading="lazy"></p>
<h4 id="heading-gradual-replacement-parallel-run">Gradual replacement (Parallel Run)</h4>
<p>If a specific component is to be replaced with a new implementation, it can help to run them in parallel for a while. For instance, if you have a spaghetti module that you want to redo correctly, you could leave the spaghetti code in place for legacy calls but start routing new calls to the new module.</p>
<p>The result is a codebase where changes in one area (hopefully) won’t unpredictably break another, a key property of a maintainable system.</p>
<h3 id="heading-6-testing-strategies-safely-refactoring-with-confidence">6. Testing Strategies (Safely Refactoring with Confidence)</h3>
<p>A robust testing strategy will give you the confidence to make sweeping changes because you’ll know quickly if something important breaks. Here’s how to approach testing in the context of a large refactoring:</p>
<h4 id="heading-establish-a-baseline-with-regression-tests">Establish a baseline with regression tests</h4>
<p>Before you even begin refactoring a particular component, make sure you have tests that cover its current behavior. You're lucky if the codebase already has a good test suite, but many legacy systems have inadequate tests.</p>
<p>One of the first tasks in those cases is often writing <strong>characterization tests</strong>. A characterization test is a test that documents what the system <em>currently does</em>, not what we think it should do​.</p>
<p>As Feathers says, “a characterization test is a test that characterizes the actual behavior of a piece of code.” This allows you to take a snapshot of what it does and ensure that it doesn’t change​.</p>
<p>This gives you a safety net so you can refactor with confidence that you’re not introducing regressions​. Use automated test suites to help things run smoothly (unit, integration, end-to-end).</p>
<h4 id="heading-continuous-integration-ci">Continuous integration (CI)</h4>
<p>It is highly recommended that testing be integrated into a CI pipeline that runs on every commit or merge. This way, you catch a bug during refactoring as soon as you introduce it, tightening the feedback loop.</p>
<h4 id="heading-canary-releases-and-feature-flags">Canary releases and feature flags</h4>
<p>Beyond pre-release testing, consider strategies for safely deploying refactored code. A canary release involves rolling out the change to a small subset of users or servers first, observing it, and then gradually expanding​.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfAif0ftiqEhiRPDygrmhtzSsfrctq6ZPfJnMg04GwKmxKk-NFiP9GjEGE9rfz7U_WKhRcBYSBYlirjKwzr-PvfZz2FJpEWS6U0UqNh-WayiVM5BGIyz3sabSX-zdKKA0j_ojvhIA?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="Canary releases and feature flags" width="600" height="400" loading="lazy"></p>
<p>This is great for catching issues that tests might miss (for example, performance issues or edge cases in production data). If the canary looks good (no errors, metrics are healthy), you proceed to full rollout. If not, you rollback quickly—with only a small impact scope.</p>
<h4 id="heading-performance-and-load-testing">Performance and load testing</h4>
<p>If performance is a concern, incorporate performance tests into your strategy. This can be done in a staging environment. You might reconsider your approach or optimize the new code if you see a significant regression.</p>
<h4 id="heading-testing-legacy-code-lacking-tests">Testing legacy code lacking tests</h4>
<p>If you’re dealing with a part of the system with zero tests (not uncommon in older code), prioritize getting at least some coverage there. There are also techniques like <strong>approval testing</strong> (where you generate output and have a human approve it as correct, then use that as a baseline for future tests). The key is not to refactor entirely in the dark; give yourself at least a flashlight in the form of tests!</p>
<p>In sum, a strong testing strategy is non-negotiable for refactoring complex systems. It’s your safety net, early warning system, and guide to know that your “cleanup” hasn’t broken anything vital.</p>
<h3 id="heading-7-refactoring-without-breaking-performance">7. Refactoring Without Breaking Performance</h3>
<p>A common concern when refactoring is whether these cleaner code changes will make my system slower or more resource-hungry. Ideally, refactoring is about the internal structure and shouldn’t change external behavior, and performance is part of the behavior.</p>
<p>In theory, performance should remain the same if you don’t change algorithms or data structures in a way that affects complexity.</p>
<p>In practice, though, performance can be inadvertently affected by refactoring. The new code may be more readable but uses more memory, or perhaps a critical caching mechanism was removed in the spirit of simplicity.</p>
<p><strong>Senior engineers need to be mindful of performance-sensitive parts of the system when refactoring and take steps to avoid regressions (or even improve performance where possible).</strong></p>
<p>Here’s how to refactor with performance in mind:</p>
<h4 id="heading-identify-performance-critical-code-paths">Identify performance-critical code paths</h4>
<p>Not all codes are equal regarding performance impact. If you refactor them, treat it almost like a functional change: you must re-measure performance afterwards. You have more leeway for parts of the code that run rarely or are not bottlenecks.</p>
<h4 id="heading-use-profiling-before-and-after">Use profiling before and after</h4>
<p>A profiler is a tool that measures where time is spent in your code or how memory is allocated. It’s beneficial to run a profiler on the code before refactoring a module to see how it behaves, and then run it after to compare. If you see, for example, that after refactoring, a function now shows up as taking 30% of execution time (when it was negligible before), that’s a red flag. Maybe the new code calls it more times than before.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> cProfile, pstats
<span class="hljs-keyword">from</span> mymodule <span class="hljs-keyword">import</span> slow_function

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">profile</span>(<span class="hljs-params">fn</span>):</span>
    profiler = cProfile.Profile()
    profiler.enable()
    fn()
    profiler.disable()
    stats = pstats.Stats(profiler).strip_dirs().sort_stats(<span class="hljs-string">'cumtime'</span>)
    stats.print_stats(<span class="hljs-number">10</span>)

<span class="hljs-comment"># run before refactor</span>
profile(<span class="hljs-keyword">lambda</span>: slow_function())

<span class="hljs-comment"># after you refactor slow_function(), re-run and compare stats</span>
</code></pre>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXd1xNcjypguN9JbN7JtBhAtBkfDrtCV6IwOORRUVT5rOAha_I2GQx3vgKRAjlxpeeUIGLTETRR6J3EnS2y95DY6ypiH95DQJT0vRfcyxv2KIz99hPXa0O8JjTzxpi5eSsk3spN6EQ?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="profiler-dashboard" width="600" height="400" loading="lazy"></p>
<h4 id="heading-when-possible-improve-performance-through-refactoring">When possible, improve performance through refactoring</h4>
<p>On the flip side, refactoring can help performance.</p>
<p>For example, by refactoring duplicated code into one place, you can use better caching in that one place. So, watch for performance improvement opportunities that arise naturally as you refactor.</p>
<p>Performance should be treated as part of the “external behavior” that needs to be preserved in a good mindset. Refactoring should ideally not make things slower for users. To ensure that, incorporate performance checks into your plan, especially for critical sections. Measure, don’t guess. The end goal is a codebase that is both clean <strong>and</strong> fast enough.</p>
<h3 id="heading-8-automate-code-reviews-with-ai-tools">8. Automate Code Reviews with AI tools</h3>
<p>Refactoring code is an ongoing process, not a one-time event – AI code review tools help enforce clean-code standards, catch smells early, and reduce the repetitive tasks that can bog down human reviewers. This frees your engineers to focus on deeper architectural or domain-specific issues.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfWs-ZM80TK_JcjwyPEnywdJl6Tf4G6gYFa1cN_J2ugTlniaGr4a397JuUj721m7kUw0EKMnzYHykpHJdG_aW7w3_B2J91bLL1UoaabdNsmH1uckMJHcFVpAhqZM2r855AsVYwDJg?key=nBTgfzmVkL2-N7DBMJ6e6gyk" alt="CodeRabbit-ai-code-reviewer-tool" width="600" height="400" loading="lazy"></p>
<p>One powerful option is <a target="_blank" href="https://www.coderabbit.ai/">CodeRabbit</a>, an AI-driven review platform designed to cut review time and bugs in half.</p>
<p>Here’s how it works and why it can boost your refactoring workflow:</p>
<h4 id="heading-ai-powered-contextual-feedback">AI-powered contextual feedback</h4>
<p>CodeRabbit analyzes pull requests line by line, applying both advanced language models and static analysis under the hood. It flags potential bugs, best-practice deviations, and style issues before a human opens the PR.</p>
<p>Some other features include:</p>
<ul>
<li><p><strong>Auto-generated summaries and 1-click fixes</strong> – Summarize large PRs and apply straightforward fixes instantly.</p>
</li>
<li><p><strong>Real-time collaboration and AI chat</strong> – Chat with the AI for clarifications, alternate code snippets, and instant feedback.</p>
</li>
<li><p><strong>Integrates with popular dev platforms</strong> – Supports GitHub, GitLab, and Azure DevOps for seamless PR scanning.</p>
</li>
</ul>
<p>CodeRabbit even has a free AI code reviews in VS Code and with this <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=CodeRabbit.coderabbit-vscode">VS Code extension</a>, you can get the most advanced AI code reviews directly in your code editor, saving review time, catching more bugs, and helping you in refactoring.</p>
<h2 id="heading-summary">Summary</h2>
<p>Refactoring a complex enterprise codebase is like renovating a large building while people still live in it without collapsing the structure.</p>
<p>Refactoring should be an ongoing process. You prevent the codebase from decaying by incorporating these practices into your regular development (perhaps allocating some time each sprint for refactoring or doing it opportunistically when touching your code). Each minor refactoring should not be too complex, and the cumulative effect is significant.</p>
<p>As <a target="_blank" href="https://martinfowler.com/">Martin Fowler</a> puts it, a series of small changes can lead to a significant improvement in design.</p>
<p>That's it for this blog. I hope you learned something new today.</p>
<p>If you want to read more interesting articles about developer tools, React, Next.js, AI and more, then I'll encourage you to checkout my <a target="_blank" href="https://www.devtoolsacademy.com/">blog</a>.</p>
<p>Some of the new and interesting articles I've written in the last 24 months.</p>
<ul>
<li><p><a target="_blank" href="https://www.devtoolsacademy.com/blog/cursor-vs-windsurf/">Cursor vs Windsurf</a></p>
</li>
<li><p><a target="_blank" href="https://clerk.com/blog/nextjs-role-based-access-control">How to Implement Role-Based Access Control in Next.js</a></p>
</li>
<li><p><a target="_blank" href="https://www.devtoolsacademy.com/blog/ai-code-reviewers-vs-human-code-reviewers/">AI Code Reviewers vs Human Code Reviewers</a></p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/how-i-built-a-custom-video-conferencing-app-with-stream-and-nextjs/">How to Build a Custom Video Conferencing App with Stream and Next.js</a></p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/how-to-perform-code-reviews-in-tech-the-painless-way/">How to Perform Code Reviews in Tech – The Painless Way</a></p>
</li>
</ul>
<p>You can get in touch if you have any questions or corrections. I’m expecting them.</p>
<p>And if you found this blog useful, please share it with your friends and colleagues who might benefit from it as well. Your support enables me to continue producing useful content for the tech community.</p>
<p>Now it’s time to take the next step by subscribing to my <a target="_blank" href="https://bytesizedbets.com/"><strong>newsletter</strong></a> and following me on <a target="_blank" href="https://twitter.com/theankurtyagi"><strong>Twitter</strong></a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Perform Code Reviews in Tech – The Painless Way ]]>
                </title>
                <description>
                    <![CDATA[ Okay, I know you may be skeptical: other guides have promised painless code reviews only to reveal that their solution requires some hyper-specific tech stack or a paid developer tool. I won’t do that to you. This guide provides a straightforward and... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-perform-code-reviews-in-tech-the-painless-way/</link>
                <guid isPermaLink="false">674f6a16678426d72d800cd5</guid>
                
                    <category>
                        <![CDATA[ code review ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Engineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ankur Tyagi ]]>
                </dc:creator>
                <pubDate>Tue, 03 Dec 2024 20:29:10 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1733242289474/def1a314-fe64-448b-9236-f66a529e3f13.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Okay, I know you may be skeptical: other guides have promised painless <code>code reviews</code> only to reveal that their solution requires some hyper-specific tech stack or a paid developer tool. I won’t do that to you.</p>
<p>This guide provides a straightforward and flexible template for <code>code reviews</code> that you can apply to your engineering team. The <strong>only</strong> requirement is that your app code is <code>open source</code>.</p>
<p>You can test a TypeScript workflow, Java workflow, Python workflow, PHP, Ruby or even some wacky web stack you invented. And it doesn’t matter if you’re developing on Windows, Linux, or Mac. Best of all, you don’t have to perform convoluted configuration or install software beyond a <code>yaml</code>.</p>
<p>I’ve been in engineering for the last 15 years, and <code>code reviews</code> have a bad reputation. We’ve all witnessed or lived through horror stories where sometimes it feels like every previous line gets torn to shreds.</p>
<p>So, what <em>can</em> you do differently? How can you make reviewing your code painless so that even the biggest nitpick on your team has nothing but praise?</p>
<p>After participating in code reviews for a decade, taking code reviews less personally is <strong>the single biggest thing you can do to improve your code.</strong> Why? Because all software is iterative. Even “perfect” code will eventually become outdated. Instead of thinking of it like a graded assignment, think of it as a part of the process.</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-what-is-a-code-review">What is a Code Review?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-the-purpose-of-a-code-review">What is the Purpose of a Code Review?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-is-doing-code-reviews-hard">Why is Doing Code Reviews Hard?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-can-ai-replace-code-reviews">Can AI Replace Code Reviews?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-to-focus-on-during-a-code-review">What to Focus on During a Code Review</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-code-review-best-practices-and-process">Code Review Best Practices And Process</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-coderabbit">What is CodeRabbit?</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-does-coderabbit-help">How Does CodeRabbit Help?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-a-github-repo-to-test">A GitHub Repo to Test</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-additional-examples">Additional Examples</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>This tutorial uses free, open-source tools. You’ll need to have a <a target="_blank" href="https://github.com/tyaga001">GitHub account</a> to help you make your code reviews more pleasant and valuable.</p>
<h2 id="heading-what-is-a-code-review"><strong>What is a Code Review?</strong></h2>
<p>The term “<a target="_blank" href="https://en.wikipedia.org/wiki/Code_review">code review</a>” can refer to various activities, from simply reading code over your teammate’s shoulder to a 10-person meeting where you dissect code line by line. I use the term to refer to a formal and written process, but not so heavyweight as a series of in-person code inspection meetings.</p>
<p>In a project where you work on a repository with other developers, after you complete your work, you commit, push, and create a pull request on the VCS, most likely using Git commands. Then, everyone reviews the pull request to determine whether it’s okay to use. If so, they approve it, and that code gets used in the project.</p>
<h2 id="heading-what-is-the-purpose-of-a-code-review"><strong>What is the Purpose of a Code Review?</strong></h2>
<p>Code Reviews are a tool for <em>knowledge transfer</em>. They help make devs more efficient when doing maintenance on a part of the system they didn't write.</p>
<p>When you review a pull request, it’s an opportunity to iron out issues before they become technical debt.</p>
<p>Code reviews can also be a good setting for mentoring junior developers.</p>
<p>Now, let’s discuss what is <strong>not</strong> the purpose of a code review:</p>
<ul>
<li>Finding bugs. That's what tests (unit, integration, e2e, api, and so on…)are for.</li>
</ul>
<p>Nitpicking on style issues – settle for one style and use formatters or AI tools to enforce it. Just keep in mind that there are many things that an AI tool cannot check. Code reviews are an excellent place to ensure the code is sufficiently documented or self-documenting.</p>
<p>Do you want to know how you can check this? Return to the code you wrote 6-12 months ago and try to understand what it was written to do.</p>
<p>If you understand it quickly, that means it's readable, and the code review was done properly and in a helpful manner.</p>
<h2 id="heading-why-is-doing-code-reviews-hard">Why is Doing <strong>Code Reviews</strong> Hard?</h2>
<p>Despite their importance, many devs don’t like doing code reviews – in part because they can be challenging, especially if you’re not following best practices.</p>
<p>Here are some pain points I’ve observed during my years of participating in code reviews:</p>
<ul>
<li><p>When people talk about code reviews, they focus on the reviewer. But the developer who writes the code is just as crucial to the review as the person who reads it.</p>
</li>
<li><p>Doing a code review is not an automatic routine for a developer.</p>
</li>
<li><p>The reviewer may sometimes just do a partial review and add new comments at every pass, even on code in the previous review(s) that remained untouched.</p>
</li>
<li><p>Sometimes, the code reviewer may not clearly express their expectations.</p>
</li>
<li><p>Multiple code reviewers can often have diverging opinions, leading to (too) long discussions.</p>
</li>
<li><p>The developer does not understand the comments from reviewers and requires back-and-forth discussions.</p>
</li>
<li><p>The developer addresses code review comments differently than agreed upon during the review process.</p>
</li>
</ul>
<p>These pain points often bottleneck our development velocity. But recent advances in AI-assisted code review tools have started addressing these common friction points in our PR workflows.</p>
<p>Let's explore how AI-powered tools, along with some best practices, can address these review challenges and optimize your development workflow.</p>
<h2 id="heading-can-ai-replace-code-reviews"><strong>Can AI Replace Code Reviews?</strong></h2>
<p>While AI hasn’t replaced human code reviews, it is a powerful force multiplier in the review process.</p>
<p>Here's how: AI code reviews excel as a preliminary screening tool, catching common issues before human reviewers see the code. This becomes especially valuable in open-source projects where maintainer bandwidth is limited.</p>
<p>I recently started using AI code reviews on a case-by-case basis for my projects.</p>
<p>AI tools improve my existing workflows, reduce failure rates by detecting logic errors early on, and boost productivity. So I’ve added it to my CI/CD pipelines. It doesn't have to be perfect at detecting logic errors, as long as its false positive rate is very low (ideally as close to 0 as possible).</p>
<p>Most importantly, AI reviews respect the golden rule of 'value your reviewer's time' by handling routine checks. This allows human reviewers to focus on architecture, business logic, and complex edge cases.</p>
<p>This approach positions AI as a complementary tool that augments rather than replaces human expertise in the code review process.</p>
<h2 id="heading-what-to-focus-on-during-a-code-review">What to Focus on During a Code Review</h2>
<p>When reviewing code, try to prioritise what matters most using the Code Review Pyramid. This is a framework that helps you focus your attention where it creates the most value.</p>
<p>Think of it like building a house — start with the foundation before worrying about paint colours.</p>
<p>The pyramid has five layers, from most critical (bottom) to least critical (top):</p>
<ol>
<li><p><strong>API Semantics</strong>: Core design decisions that affect users</p>
</li>
<li><p><strong>Implementation Semantics</strong>: The code's functionality, security, and performance</p>
</li>
<li><p><strong>Documentation</strong>: Clear explanation of how to use the code</p>
</li>
<li><p><strong>Tests</strong>: Verification that everything works as intended</p>
</li>
<li><p><strong>Code Style</strong>: Formatting and naming conventions</p>
</li>
</ol>
<p>Source: <a target="_blank" href="https://www.morling.dev/blog/the-code-review-pyramid/">The Code Review Pyramid by Gunnar Morling</a></p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/gunnarmorling/status/1501645187407388679"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p> </p>
<p>Remember: if you want to catch issues/bugs, there are more appropriate processes for that. That is why we have automated testing, canary releases, testing environments, and so on.</p>
<p>In my personal opinion, using code reviews as a bug catching tool is somewhat of an anti-pattern where you're compensating for a development process that may be lacking some key steps/processes.</p>
<p>To me, a <code>code review</code> is much more about managing technical <code>debt</code> and ensuring that quality is produced, while shipping more features.</p>
<p>In doing a code review, you should make sure that:</p>
<ul>
<li><p>The code is readable</p>
</li>
<li><p>It has appropriate unit tests</p>
</li>
<li><p>The developer used clear names for everything</p>
</li>
<li><p>The code is well-designed and isn’t more complex than it needs to be</p>
</li>
<li><p>Test cases make sense and have comprehensive coverage</p>
</li>
<li><p>It’s something the team can maintain in the long run</p>
</li>
<li><p>There are no architectural issues that will block the team</p>
</li>
<li><p>The code fits the team's idea of quality</p>
</li>
<li><p>You’re thinking about what you can learn from the PR</p>
</li>
<li><p>You’re sharing any knowledge the developer might use in their PR</p>
</li>
<li><p>You’re thinking about how you can empower the dev through your positive feedback</p>
</li>
<li><p>The PR has a clear changelist description</p>
</li>
</ul>
<h2 id="heading-code-review-best-practices-and-process"><strong>Code Review Best Practices And Process</strong></h2>
<p>There is no general rule in engineering for code reviews, as what you’ll need to focus on depends on many factors. You can and should set up the process according to your company standards and way of working as a team.</p>
<p>Here are some factors you’ll need to think about before setting up a code review process:</p>
<ul>
<li><p>The size and type of company you’re in (for example a startup vs a large corporation)</p>
</li>
<li><p>The number of developers on your team</p>
</li>
<li><p>Your budget</p>
</li>
<li><p>The timeframe you’re working with</p>
</li>
<li><p>Your and your team’s workloads</p>
</li>
<li><p>The complexity of the code</p>
</li>
<li><p>The abilities and skills of the reviewer(s)</p>
</li>
<li><p>The availability of the reviewer(s)</p>
</li>
</ul>
<p>As an example, at my work we have a very simple rule: <strong>all</strong> <strong>code</strong> <strong>changes must be reviewed by at least one developer</strong> before a merge or a commit to the trunk.</p>
<p>Code reviews need a systematic approach, but maintaining consistency across every PR is challenging. It’s useful to let computers handle repetitive checks (style, formatting) while humans focus on what matters most: architecture and logic. This balanced approach makes reviews both thorough and sustainable.</p>
<p><strong>Take a look at this example</strong>. It shows how we can optimize our <code>code review</code> process by intelligently delegating tasks between humans and automated tools. The diagram below illustrates a typical code style review workflow, comparing manual human review steps against automated tooling.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731490662335/8b0e9e27-c31b-409f-9c9e-fd1a33195d9b.png" alt="Human vs Automated Code Style Review Process - showing why formatting checks should be automated" class="image--center mx-auto" width="1287" height="2074" loading="lazy"></p>
<p>The diagram shows a real problem we all face in code reviews. See the left side? That's we humans doing manual formatting checks: finding weird spaces, fixing indents, writing comments about it... pretty tedious stuff. But check out the right side: that's where tools like <code>Prettier</code> just fix these formatting issues automatically.</p>
<p>No meetings, no back-and-forth – just done. That's why I started using <code>CodeRabbit</code>, which is a dev tool that caught my attention recently.</p>
<h2 id="heading-what-is-coderabbit"><strong>What is CodeRabbit?</strong></h2>
<p>The CodeRabbit docs describe the tool pretty effectively, so I’ll just leave this here:</p>
<blockquote>
<p><a target="_blank" href="https://www.coderabbit.ai/"><strong>CodeRabbit</strong></a> is an AI-powered code reviewer that delivers context-aware feedback on pull requests within minutes, reducing the time and effort needed for manual code reviews. It provides a fresh perspective and catches issues that are often missed, enhancing the overall review quality. – from the CodeRabbit docs</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731326629130/933c46f2-a24c-4e08-a470-8449e96387aa.png" alt="what is CodeRabbit - home page" class="image--center mx-auto" width="3084" height="1850" loading="lazy"></p>
<h3 id="heading-how-does-coderabbit-help">How Does CodeRabbit Help?</h3>
<p>Let me walk you through a real example. When you submit a PR, CodeRabbit:</p>
<ol>
<li>Performs a PR summary on the fly:</li>
</ol>
<ul>
<li><p>First, it gives you a quick overview of what changed.</p>
</li>
<li><p>It also explains the impact in plain English (great for non-tech folks in your team).</p>
</li>
<li><p>Then it includes a detailed walkthrough of file changes.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732879970322/c671b932-25b1-474c-8cae-c393cb1706b8.png" alt="Pull Request Summary" class="image--center mx-auto" width="2462" height="1356" loading="lazy"></p>
<ol start="2">
<li>Does a “Smart Code Review”:</li>
</ol>
<ul>
<li><p>It drops comments right on the specific lines that need attention.</p>
</li>
<li><p>It also suggests fixes in diff format that you can apply them with one click.</p>
</li>
<li><p>And it shows what commits and files it checked (which is helpful for tracking review coverage).</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732880687958/8d0e1ce5-cb23-4c62-b9ba-823f3a59845e.png" alt="Smart Code Reviews" class="image--center mx-auto" width="1952" height="1614" loading="lazy"></p>
<ol start="3">
<li>Give you interactive feedback:</li>
</ol>
<ul>
<li><p>You can chat with it right in the PR comments.</p>
</li>
<li><p>You can ask it questions about specific code changes to get more details.</p>
</li>
<li><p>And it remembers your team's patterns and preferences which is super helpful for consistency’s sake (which I discussed above).</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732880617364/9e246445-1d43-45f1-b4af-62d9f013d76a.png" alt="chat with coderabbit" class="image--center mx-auto" width="1930" height="1672" loading="lazy"></p>
<ol start="4">
<li>Extra Helpful Features:</li>
</ol>
<ul>
<li><p>CodeRabbit validates changes against linked GitHub/GitLab issues.</p>
</li>
<li><p>It creates sequence diagrams to visualize changes.</p>
</li>
<li><p>And it can perform one-click fixes on applications for simple issues.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732880539024/412d6c15-d691-4b65-b335-2e04b04a55e1.png" alt="sequence diagrams by coderabbit" class="image--center mx-auto" width="1966" height="1458" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731322941721/9e7c5e9a-ac02-458b-9de3-4cf92232786d.png" alt="Code reviews done by CodeRabbit" class="image--center mx-auto" width="2834" height="1842" loading="lazy"></p>
<p>I first discovered <code>CodeRabbit</code> last month while I was searching for something else on GitHub. I accidentally came across it and I was surprised by how many people are already using it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731323088015/12db3391-bad0-45a7-908d-2c34391a7803.png" alt="how many projects are already using coderabbit" width="3142" height="1842" loading="lazy"></p>
<p>I instantly signed up because I was looking for exactly such a solution which could help me and my team out with our reviews.</p>
<p>I read through <a target="_blank" href="https://docs.coderabbit.ai/">the CodeRabbit docs</a> and was very impressed.</p>
<p>Getting started using it is pretty much a plug and play process.</p>
<p>In the next section, we’ll go through the quick steps you can follow to enable CodeRabbit using an example repo.</p>
<ul>
<li><p>Sign up at <a target="_blank" href="http://coderabbit.ai">coderabbit.ai</a> using your GitHub account.</p>
</li>
<li><p>Go to Add Your Repository.</p>
</li>
<li><p>And that's it. CodeRabbit starts reviewing your PRs automatically.</p>
</li>
</ul>
<h3 id="heading-a-github-repo-to-test"><strong>A GitHub Repo to Test</strong></h3>
<p>As an example <strong>GitHub</strong> <strong>repo</strong> to test, we’ll use <a target="_blank" href="https://www.devtoolsacademy.com/">devtoolsacademy</a>: my blog on everything about awesome developer tools.</p>
<p>First, visit the <a target="_blank" href="https://app.coderabbit.ai/login">CodeRabbit login page</a> and login via GitHub.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732880133507/959c0521-eddf-4026-bf33-64b415f4d9b3.png" alt="login - coderabbit" class="image--center mx-auto" width="1462" height="1172" loading="lazy"></p>
<p>Next, add CodeRabbit to some of your public GitHub repositories.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731327118318/7329afd5-af9c-4e54-9aba-6720cd00b8ca.png" alt="how-to-add-a-public-repo-to-use-coderabbit" class="image--center mx-auto" width="3126" height="1804" loading="lazy"></p>
<p>Now, CodeRabbit is fully integrated and ready to do code reviews on your selected repo.</p>
<p>Yes: it’s that simple and fast. And in my opinion, it’s one of the main reasons the tool is so useful.</p>
<p>Here are some sample PRs for you to check out:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/tyaga001/devtoolsacademy/pull/10">https://github.com/tyaga001/devtoolsacademy/pull/10</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/tyaga001/devtoolsacademy/pull/13">https://github.com/tyaga001/devtoolsacademy/pull/13</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/sartography/spiff-arena/pull/1233#discussion_r1529013218">sartography/spiff-arena#1233 (comment)</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/sartography/spiff-arena/pull/1233#discussion_r1529013218">kmesiab/equilibria#1 (comment</a><a target="_blank" href="https://github.com/kmesiab/equilibria/pull/1#discussion_r1505474270">)</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/kmesiab/equilibria/pull/1#discussion_r1505474270">kamiazya/web-csv-toolbox#60</a> <a target="_blank" href="https://github.com/kamiazya/web-csv-toolbox/pull/60#discussion_r1453463448">(comment)</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/kamiazya/web-csv-toolbox/pull/60#discussion_r1453463448">openreplay/openreplay#1858 (comme</a><a target="_blank" href="https://github.com/openreplay/openreplay/pull/1858#discussion_r1467629285">nt)</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/openreplay/openreplay/pull/1858#discussion_r1467629285">ls1intum/Artemis#8037 (comm</a><a target="_blank" href="https://github.com/ls1intum/Artemis/pull/8037#discussion_r1494109998">ent)</a></p>
</li>
</ul>
<h3 id="heading-additional-examples"><strong>Additional Examples</strong></h3>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">check all the open source examples of code reviews done by <a target="_self" href="https://github.com/search?q=coderabbitai&amp;type=pullrequests">CodeRabbit</a>.</div>
</div>

<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Everyone’s code needs reviewing. Just because someone is the most senior person on the team does not mean that their code doesn’t need to be reviewed.</p>
<p>In this article, I talked about code reviews along with some common pain points. I then showed you how you can leverage CodeRabbit to iterate quickly through your code reviews and focus more on business.</p>
<h3 id="heading-further-reading"><strong>Further reading</strong></h3>
<p>In this article I talked about basic intro to CodeRabbit, because that was my use case with my <a target="_blank" href="https://www.devtoolsacademy.com/">blog</a>.</p>
<p>For more advanced functionality, check out the official CodeRabbit <a target="_blank" href="https://docs.coderabbit.ai/">docs</a> or read their <a target="_blank" href="https://www.coderabbit.ai/blog">blog</a>.</p>
<h3 id="heading-before-i-end"><strong>Before I End</strong></h3>
<p>I hope you found it helpful learning how to use AI tools for code reviews.</p>
<p>If you like my writing, these are some of my other most recent articles.</p>
<ul>
<li><p><a target="_blank" href="https://www.devtoolsacademy.com/blog/neon-vs-supabase"><strong>Neon Postgres vs Supabase</strong></a></p>
</li>
<li><p><a target="_blank" href="https://www.devtoolsacademy.com/blog/mongoDB-vs-postgreSQL"><strong>MongoDB vs. PostgreSQL</strong></a></p>
</li>
<li><p><a target="_blank" href="https://www.devtoolsacademy.com/blog/supabase-vs-clerk">Supabase vs Clerk</a></p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/how-i-built-a-custom-video-conferencing-app-with-stream-and-nextjs/#heading-next-steps">How I Built a Video Conferencing App with Stream and Next.js</a></p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/fine-grained-authorization-in-java-and-springboot/">How to Implement Fine-Grained Authorization in Java</a></p>
</li>
</ul>
<p>Follow me on <a target="_blank" href="https://x.com/theankurtyagi">Twitter</a> to stay updated on my open source projects.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Implement Fine-Grained Authorization in Java and SpringBoot ]]>
                </title>
                <description>
                    <![CDATA[ Securing your application goes beyond simply granting or denying access at the surface level. As a developer, you need to implement fine-grained authorization (FGA) to manage permissions at a more detailed, granular level. FGA allows you to set up de... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/fine-grained-authorization-in-java-and-springboot/</link>
                <guid isPermaLink="false">671a85db0db570158155ec00</guid>
                
                    <category>
                        <![CDATA[ Java ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Springboot ]]>
                    </category>
                
                    <category>
                        <![CDATA[ authorization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Developer ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Developer Tools ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ankur Tyagi ]]>
                </dc:creator>
                <pubDate>Thu, 24 Oct 2024 17:37:31 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729783227124/9725e8cf-553d-42c3-a823-5215e8d4d0e9.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Securing your application goes beyond simply granting or denying access at the surface level. As a developer, you need to implement <code>fine-grained authorization</code> (FGA) to manage permissions at a more detailed, granular level.</p>
<p>FGA allows you to set up detailed access controls that specify who can do what and under which conditions.</p>
<p>In this tutorial, you will learn how to implement <code>fine-grained authorization</code> in Java and Spring Boot using <a target="_blank" href="https://permit.io/">Permit.io</a>.</p>
<p>Here is the <a target="_blank" href="https://github.com/tyaga001/java-spring-fine-grained-auth">source code</a> (remember to give it a star ⭐).</p>
<p>I hope you enjoyed my previous <a target="_blank" href="https://www.freecodecamp.org/news/how-i-built-a-custom-video-conferencing-app-with-stream-and-nextjs/">blog</a> about building a custom video conferencing app with Stream and Next.js. These blogs reflect my journey in creating DevTools Academy, a platform designed to help developers discover amazing developer tools.</p>
<p>This tutorial is another effort to introduce you to a super helpful developer tool that I recently explored.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents:</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-permit">What is Permit</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-what-is-fine-grained-authorization">What is Fine-Grained Authorization</a>?</p>
<ul>
<li><p><a class="post-section-overview" href="#heading-role-based-access-control-rbac">Role-Based Access Control (RBAC)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-attribute-based-access-control-abac">Attribute-Based Access Control (ABAC)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-relationship-based-access-control-rebac">Relationship-Based Access Control (ReBAC)</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-implement-fine-grained-authorization">How to Implement Fine-Grained Authorization</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-implementing-role-based-access-control">Implementing Role-Based Access Control</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-implementing-attribute-based-access-control">Implementing Attribute-Based Access Control</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-implementing-relationship-based-access-control">Implementing Relationship-Based Access Control</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-implement-fga-in-java-and-springboot">How to Implement FGA in Java and SpringBoot</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-step-1-setting-up-the-e-commerce-application">Step 1: Setting Up the E-commerce Application</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-2-get-your-environment-api-key">Step 2: Get your Environment API Key</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-3-deploy-policy-decision-point-pdp">Step 3: Deploy Policy Decision Point (PDP)</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-step-4-running-the-app">Step 4: Running the App</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-next-steps">Next Steps</a></p>
<ul>
<li><a class="post-section-overview" href="#heading-before-we-end">Before We End...</a></li>
</ul>
</li>
</ul>
<h2 id="heading-what-is-permit"><strong>What is</strong> Permit?</h2>
<blockquote>
<p><a target="_blank" href="https://www.permit.io">Permit.io</a> is a full stack, plug-and-play application-level authorization solution that allows you to implement a <code>secure</code>, <code>flexible</code>, <code>authorization</code> layer within minutes, so you can focus on what matters most.</p>
</blockquote>
<p><a target="_blank" href="https://www.permit.io"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729499767197/6e2b4312-8986-493e-9453-3b67e6aad155.png" alt="what is permit - screenshot of homepage" class="image--center mx-auto" width="600" height="400" loading="lazy"></a></p>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>To fully understand the tutorial, you need to have a basic understanding of <code>Java</code> and <code>Spring Boot</code>. You’ll also need the following:</p>
<ul>
<li><p><a target="_blank" href="http://Permit.io"><strong>Permit.io</strong></a>: A developer tool that simplifies the implementation of FGA.</p>
</li>
<li><p><strong>Spring Boot Starter Web</strong>: Provides essential components for building web applications, including RESTful APIs.</p>
</li>
<li><p><strong>Gradle</strong>: A build tool for managing dependencies.</p>
</li>
<li><p><strong>JDK 11 or later</strong>: The Java Development Kit version required to compile and run your Spring Boot app.</p>
</li>
<li><p><strong>Postman or cURL</strong>: Tools for testing your <code>API</code> endpoints.</p>
</li>
</ul>
<h2 id="heading-what-is-fine-grained-authorization"><strong>What is Fine-Grained Authorization?</strong></h2>
<p><a target="_blank" href="https://www.permit.io/blog/what-is-fine-grained-authorization-fga">Fine-grained authorization</a> offers access control to resources by determining who can access them, to what extent, and under specified conditions.</p>
<p>Contrary to coarse-grained authorization (that handles access based on categories like <code>user roles</code> such as "<code>admin</code>" or "<code>user</code>"), fine-grained authorization gives you the flexibility to define access at a granular level, for specific resources or actions and even attributes. </p>
<p>In <code>Fine Grained Authorization</code> there exist 3 types of policy models for managing authorization; <strong>Role Based Access Control (RBAC)</strong>, <strong>Attribute Based Access Control (ABAC)</strong>, and <strong>Relationship-Based Access Control (ReBAC)</strong>.</p>
<p>Let's take a look, at each of these approaches and see how you can implement them in your application.</p>
<h3 id="heading-role-based-access-control-rbac"><strong>Role-Based Access Control (RBAC)</strong></h3>
<p><a target="_blank" href="https://www.permit.io/blog/what-is-rebac">RBAC</a> is a security approach that controls resource access based on the roles of users within an organization. This model streamlines permissions by organizing users into roles and managing access control according to these defined roles.</p>
<p><strong>Key Concepts in RBAC:</strong></p>
<p><strong>Users:</strong> People who use the system such as employees or customers.</p>
<p><strong>Roles:</strong> A set of permissions or access privileges assigned to a group of users based on their responsibilities or tasks such as admin, manager, or customer.</p>
<p><strong>Permissions:</strong> The rights granted to users for interacting with resources, such as read, write, or delete.</p>
<h3 id="heading-attribute-based-access-control-abac"><strong>Attribute-Based Access Control (ABAC)</strong></h3>
<p><a target="_blank" href="https://www.permit.io/blog/what-is-abac">ABAC</a> is a versatile and adaptive access control model that decides who can or cannot access resources based on attributes, like user details. The ABAC model allows you to define fine-grained authorization based on user attributes.</p>
<p><strong>Key Concepts in ABAC:</strong></p>
<p><strong>Attributes:</strong> Characteristics or properties used to make access control decisions. Attributes are typically categorized into:</p>
<ul>
<li><p><strong>User Attributes:</strong> Information about the user (for example, role, department, job title, age, and so on).</p>
</li>
<li><p><strong>Resource Attributes:</strong> Characteristics of the resource (for example, file type, data classification level, creation date, owner).</p>
</li>
<li><p><strong>Action Attributes:</strong> The action the user is trying to perform (for example, read, write, delete, approve).</p>
</li>
<li><p><strong>Environmental Attributes:</strong> Contextual information about the access request (for example, time of day, location, device type, IP address).</p>
</li>
</ul>
<h3 id="heading-relationship-based-access-control-rebac"><strong>Relationship-Based Access Control (ReBAC)</strong></h3>
<p>ReBAC is an access control system that grants permissions to access resources based on the relationship between entities within a system. The approach emphasizes defining and managing access control by mapping out how users relate to resources and other entities such as organizations or groups.</p>
<p><strong>Key Concepts of ReBAC:</strong></p>
<p><strong>Entities:</strong> Users, resources (such as files and documents), and other entities, such as groups or organizational units.</p>
<p><strong>Relationships:</strong> The connections that specify the relationship between two entities. A user might be the "owner" of a document or a "member" of a team, for instance.</p>
<p><strong>Policies:</strong> Rules that use relationships to determine access rights. A user can access a resource or execute an action on it if they have a particular relationship with it.</p>
<h2 id="heading-how-to-implement-fine-grained-authorization"><strong>How to Implement Fine-Grained Authorization</strong></h2>
<p>Now that you have a basic understanding of <code>RBAC</code>, <code>ABAC</code>, and <code>ReBAC</code>, let’s see how we can implement these models in an e-commerce app.</p>
<h3 id="heading-implementing-role-based-access-control"><strong>Implementing Role-Based Access Control</strong></h3>
<p><strong>Step 1:</strong> Navigate to <a target="_blank" href="http://Permit.io">Permit.io</a>, and then create an account and your workspace.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729494514537/a49035e8-1eb2-495f-acee-6ac212d0076e.png" alt="Permit.io - create your workspace page" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>By default, you should see a project that includes two environments: <code>Development</code> and <code>Production</code>. </p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><strong><em>Note:</em></strong><em> You need to define and test your policies in the development environment before deploying them to production.</em></div>
</div>

<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729494835226/8d0e6841-09e7-44e2-89fb-1ec3bacb316d.png" alt="Permit.io - project dashboard" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><strong>Step 2:</strong> Create a resource named <strong>Products</strong>. To create the resource, open the <strong>Policy</strong> tab on the left sidebar and then open the <strong>Resources</strong> tab at the top. After that, click the <strong>Create a Resource</strong> button and then create a resource called <strong>Products</strong> with actions <code>read</code>, <code>create</code>, <code>update</code>, and <code>delete</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729495042599/91660d3d-eafe-4874-aeb2-50bf88c5a291.png" alt="Permit.io - how to add a new resource" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><strong>Step 3:</strong> Create another resource called <strong>Reviews</strong> with actions <code>read</code>, <code>create</code>, <code>update</code>, and <code>delete</code>.</p>
<p><strong>Step 4:</strong> Open the <strong>Policy Editor</strong> tab. You’ll see that 3 roles named <code>admin</code>, <code>editor</code>, and <code>viewer</code> were created.</p>
<ul>
<li><p>Role admin has permission to <code>create</code>, <code>delete</code>, <code>read</code>, or <code>update</code> a product or a review.</p>
</li>
<li><p>Role <code>editor</code> has permission to <code>create</code>, <code>read</code>, or <code>update</code> a <code>product</code> or a <code>review</code> but not <code>delete</code> any.</p>
</li>
<li><p>Role <code>viewer</code> has permission to <code>create</code> and <code>read</code> a product or a <code>review</code> but not <code>delete</code> or <code>update</code> any.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729495227714/38553c90-5cc0-4fa0-a116-2f5051b5ebb8.png" alt="Permit.io - Policy editor" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-implementing-attribute-based-access-control"><strong>Implementing Attribute-Based Access Control</strong></h3>
<p><strong>Step 1:</strong> Open the <strong>Resources</strong> tab, then click the <strong>Add Attributes</strong> button.</p>
<ul>
<li><p>Add an attribute called <strong>vendor</strong></p>
</li>
<li><p>Add an attribute called the <strong>customer</strong></p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729495417262/94a09532-83b3-496c-8fef-6ee7c836a211.png" alt="Permit.io - edit resource" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><strong>Step 2:</strong> Open the ABAC Rules tab, then create a new ABAC Resource Set called <strong>Own Products</strong> that depends on the Products resource. After that, add a condition that gives permissions only to the user who created a product based on the vendor attribute.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729495597939/d528f47f-710a-4bd6-b13a-1cf3a3c49031.png" alt="Permit.io - create your resource set page" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><strong>Step 3:</strong> Create another ABAC Resource Set called <strong>Own Reviews</strong> that depends on the Reviews resource.</p>
<h3 id="heading-implementing-relationship-based-access-control"><strong>Implementing Relationship-Based Access Control</strong></h3>
<p><strong>Step 1:</strong> Open the Resources tab and edit the Products resource. Add role <code>vendor</code> in the <code>ReBAC</code> options section. Then set products as parent of reviews in the relations section.</p>
<p><strong>Step 2:</strong> Edit the Reviews resource by adding role customer in the <code>ReBAC</code> options section, as shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729497355241/4f1a6235-7181-468a-82ce-c727df517604.png" alt="Permit.io - ABAC edit resource" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><strong>Step 3:</strong> Go to <code>Policy</code> <code>Editor</code> tab and add:</p>
<ul>
<li><p>role <code>vendor</code> permission to update and delete own products.</p>
</li>
<li><p>role <code>customer</code> permission to update and delete their own reviews on products.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729497438508/935f79b7-3789-4047-b7b5-73e82654b617.png" alt="Permit.io - Policy editor" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-implement-fga-in-java-and-springboot"><strong>How to Implement FGA in Java and SpringBoot</strong></h2>
<p>Now that we have defined <code>RBAC</code>, <code>ABAC</code>, and <code>ReBAC</code> policies in the Permit.io web interface, let’s learn how to enforce them in an E-Commerce Management System application using the Permit.io API.</p>
<p>There’s a lot of code coming up, so make sure you read through the extensive comments I’ve left throughout each code block. These will help you understand more fully what’s going on in this code.</p>
<h3 id="heading-step-1-setting-up-the-e-commerce-application"><strong>Step 1: Setting Up the E-commerce Application</strong></h3>
<p>To set up the e-commerce application and git clone the source code.</p>
<pre><code class="lang-powershell">git clone https://github.com/tyaga001/java<span class="hljs-literal">-spring</span><span class="hljs-literal">-fine</span><span class="hljs-literal">-grained</span><span class="hljs-literal">-auth</span>.git
</code></pre>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Then open the code in your Java IDE.&nbsp;I used <a target="_blank" href="https://www.jetbrains.com/idea/">JetBrains</a> for all my work.</div>
</div>

<h4 id="heading-installing-permit-package-sdk"><strong>Installing Permit package SDK</strong></h4>
<p>To install the Permit package SDK, you add the SDK under the dependencies block in the <code>build.graddle</code> file.</p>
<pre><code class="lang-java">## Dependencies

To set up the necessary dependencies <span class="hljs-keyword">for</span> your Spring Boot project, include the following in your `build.gradle` file:

```groovy
dependencies {
    implementation <span class="hljs-string">'org.springframework.boot:spring-boot-starter-web'</span>
    implementation <span class="hljs-string">'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'</span>
    developmentOnly <span class="hljs-string">'org.springframework.boot:spring-boot-devtools'</span>
    testImplementation <span class="hljs-string">'org.springframework.boot:spring-boot-starter-test'</span>
    testRuntimeOnly <span class="hljs-string">'org.junit.platform:junit-platform-launcher'</span>

    <span class="hljs-comment">// Add this line to install the Permit.io Java SDK in your project</span>
    implementation <span class="hljs-string">'io.permit:permit-sdk-java:2.0.0'</span>
}
</code></pre>
<h4 id="heading-initializing-the-permit-sdk"><strong>Initializing the Permit SDK</strong></h4>
<p>You can initialize the Permit <code>SDK</code> Client using the code below:</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.boostmytool.store.config;

<span class="hljs-keyword">import</span> io.permit.sdk.Permit;
<span class="hljs-keyword">import</span> io.permit.sdk.PermitConfig;
<span class="hljs-keyword">import</span> org.springframework.beans.factory.annotation.Value;
<span class="hljs-keyword">import</span> org.springframework.context.annotation.Bean;
<span class="hljs-keyword">import</span> org.springframework.context.annotation.Configuration;

<span class="hljs-meta">@Configuration</span>  <span class="hljs-comment">// Marks this class as a configuration class for Spring IoC</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PermitClientConfig</span> </span>{

    <span class="hljs-meta">@Value("${permit.api-key}")</span>  <span class="hljs-comment">// Inject Permit API key from application properties</span>
    <span class="hljs-keyword">private</span> String apiKey;

    <span class="hljs-meta">@Value("${permit.pdp-url}")</span>  <span class="hljs-comment">// Inject Permit PDP (Policy Decision Point) URL from application properties</span>
    <span class="hljs-keyword">private</span> String pdpUrl;

    <span class="hljs-comment">/**
     * Creates a Permit client bean with custom configuration
     * <span class="hljs-doctag">@return</span> Permit client instance
     */</span>
    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Permit <span class="hljs-title">permit</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Permit(
                <span class="hljs-keyword">new</span> PermitConfig.Builder(apiKey)  <span class="hljs-comment">// Initialize PermitConfig with API key</span>
                        .withPdpAddress(pdpUrl)   <span class="hljs-comment">// Set the PDP address</span>
                        .withDebugMode(<span class="hljs-keyword">true</span>)      <span class="hljs-comment">// Enable debug mode for detailed logging</span>
                        .build()                  <span class="hljs-comment">// Build the PermitConfig object</span>
        );
    }
}
</code></pre>
<h4 id="heading-syncing-users-with-sdk"><strong>Syncing Users with SDK</strong></h4>
<p>To start enforcing permissions, you should first sync a user to Permit, and then assign them a role.</p>
<p>In the code below, the <strong>UserService</strong> class provides methods for user login, signup, role assignment, and authorization, with exception handling for possible errors when interacting with the Permit API.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.boostmytool.store.service;

<span class="hljs-keyword">import</span> com.boostmytool.store.exception.ForbiddenAccessException;
<span class="hljs-keyword">import</span> com.boostmytool.store.exception.UnauthorizedException;
<span class="hljs-keyword">import</span> io.permit.sdk.Permit;
<span class="hljs-keyword">import</span> io.permit.sdk.api.PermitApiError;
<span class="hljs-keyword">import</span> io.permit.sdk.api.PermitContextError;
<span class="hljs-keyword">import</span> io.permit.sdk.enforcement.Resource;
<span class="hljs-keyword">import</span> io.permit.sdk.enforcement.User;
<span class="hljs-keyword">import</span> org.springframework.stereotype.Service;

<span class="hljs-keyword">import</span> java.io.IOException;

<span class="hljs-meta">@Service</span>  <span class="hljs-comment">// Marks this class as a Spring service, making it a candidate for component scanning</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserService</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Permit permit;

    <span class="hljs-comment">// Constructor injection for the Permit SDK</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">UserService</span><span class="hljs-params">(Permit permit)</span> </span>{
        <span class="hljs-keyword">this</span>.permit = permit;
    }

    <span class="hljs-comment">/**
     * Simulates user login by creating and returning a Permit User object.
     * 
     * <span class="hljs-doctag">@param</span> key User's unique key
     * <span class="hljs-doctag">@return</span> User object
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">login</span><span class="hljs-params">(String key)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> User.Builder(key).build();
    }

    <span class="hljs-comment">/**
     * Handles user signup by creating and syncing a new Permit User.
     * 
     * <span class="hljs-doctag">@param</span> key User's unique key
     * <span class="hljs-doctag">@return</span> Created and synced User object
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> User <span class="hljs-title">signup</span><span class="hljs-params">(String key)</span> </span>{
        <span class="hljs-keyword">var</span> user = <span class="hljs-keyword">new</span> User.Builder(key).build();
        <span class="hljs-keyword">try</span> {
            permit.api.users.sync(user);  <span class="hljs-comment">// Syncs the new user with the Permit service</span>
        } <span class="hljs-keyword">catch</span> (PermitContextError | PermitApiError | IOException e) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">"Failed to create user"</span>, e);  <span class="hljs-comment">// Handles exceptions during user creation</span>
        }
        <span class="hljs-keyword">return</span> user;
    }

    <span class="hljs-comment">/**
     * Assigns a role to the user within the "default" environment.
     * 
     * <span class="hljs-doctag">@param</span> user User object to assign the role to
     * <span class="hljs-doctag">@param</span> role Role to be assigned
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">assignRole</span><span class="hljs-params">(User user, String role)</span> </span>{
        <span class="hljs-keyword">try</span> {
            permit.api.users.assignRole(user.getKey(), role, <span class="hljs-string">"default"</span>);  <span class="hljs-comment">// Assigns role in the "default" environment</span>
        } <span class="hljs-keyword">catch</span> (PermitApiError | PermitContextError | IOException e) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">"Failed to assign role to user"</span>, e);  <span class="hljs-comment">// Handles exceptions during role assignment</span>
        }
    }

    <span class="hljs-comment">/**
     * Checks if the user is authorized to perform a specific action on a resource.
     * 
     * <span class="hljs-doctag">@param</span> user User object requesting authorization
     * <span class="hljs-doctag">@param</span> action Action to be authorized
     * <span class="hljs-doctag">@param</span> resource Resource on which the action will be performed
     * <span class="hljs-doctag">@throws</span> UnauthorizedException if user is not logged in
     * <span class="hljs-doctag">@throws</span> ForbiddenAccessException if user is denied access
     */</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">authorize</span><span class="hljs-params">(User user, String action, Resource resource)</span> </span>{
        <span class="hljs-keyword">if</span> (user == <span class="hljs-keyword">null</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UnauthorizedException(<span class="hljs-string">"Not logged in"</span>);  <span class="hljs-comment">// Throws exception if user is not logged in</span>
        }
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">var</span> permitted = permit.check(user, action, resource);  <span class="hljs-comment">// Performs authorization check</span>
            <span class="hljs-keyword">if</span> (!permitted) {
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ForbiddenAccessException(<span class="hljs-string">"Access denied"</span>);  <span class="hljs-comment">// Throws exception if access is denied</span>
            }
        } <span class="hljs-keyword">catch</span> (PermitApiError | IOException e) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">"Failed to authorize user"</span>, e);  <span class="hljs-comment">// Handles exceptions during authorization</span>
        }
    }
}
</code></pre>
<p>Then in the code below, the <strong>UserController</strong> class exposes REST API endpoints for user signup and role assignment. It interacts with the UserService class to handle user-related business logic and provides appropriate HTTP responses.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.boostmytool.store.controllers;

<span class="hljs-keyword">import</span> com.boostmytool.store.exception.UnauthorizedException;
<span class="hljs-keyword">import</span> com.boostmytool.store.service.UserService;
<span class="hljs-keyword">import</span> io.permit.sdk.enforcement.User;
<span class="hljs-keyword">import</span> jakarta.servlet.http.HttpServletRequest;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.PostMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RequestBody;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RequestMapping;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.RestController;

<span class="hljs-meta">@RestController</span>  <span class="hljs-comment">// Indicates that this class handles HTTP requests and returns JSON responses</span>
<span class="hljs-meta">@RequestMapping("/api/users")</span>  <span class="hljs-comment">// Base URL path for all user-related operations</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserController</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> UserService userService;

    <span class="hljs-comment">// Constructor injection of UserService, containing business logic for user operations</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">UserController</span><span class="hljs-params">(UserService userService)</span> </span>{
        <span class="hljs-keyword">this</span>.userService = userService;
    }

    <span class="hljs-comment">/**
     * Handles user signup requests.
     * Endpoint: POST /api/users/signup
     * 
     * <span class="hljs-doctag">@param</span> key Unique key for the new user
     * <span class="hljs-doctag">@return</span> Created User object
     */</span>
    <span class="hljs-meta">@PostMapping("/signup")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> User <span class="hljs-title">signup</span><span class="hljs-params">(<span class="hljs-meta">@RequestBody</span> String key)</span> </span>{
        <span class="hljs-keyword">return</span> userService.signup(key);  <span class="hljs-comment">// Calls the signup method in UserService to create a new user</span>
    }

    <span class="hljs-comment">/**
     * Handles assigning a role to the logged-in user.
     * Endpoint: POST /api/users/assign-role
     * 
     * <span class="hljs-doctag">@param</span> request HTTP request, used to retrieve the current user
     * <span class="hljs-doctag">@param</span> role Role to be assigned to the current user
     */</span>
    <span class="hljs-meta">@PostMapping("/assign-role")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">assignRole</span><span class="hljs-params">(HttpServletRequest request, <span class="hljs-meta">@RequestBody</span> String role)</span> </span>{
        <span class="hljs-comment">// Retrieves the current user from the request attributes</span>
        User currentUser = (User) request.getAttribute(<span class="hljs-string">"user"</span>);

        <span class="hljs-comment">// Throws an exception if the user is not logged in</span>
        <span class="hljs-keyword">if</span> (currentUser == <span class="hljs-keyword">null</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UnauthorizedException(<span class="hljs-string">"Not logged in"</span>);
        }

        <span class="hljs-comment">// Assigns the specified role to the current user</span>
        userService.assignRole(currentUser, role);
    }
}
</code></pre>
<h4 id="heading-creating-rbac-abac-and-rebac-policy-enforcement-point"><strong>Creating RBAC, ABAC, and ReBAC Policy Enforcement Point</strong></h4>
<p>In the code below, the <strong>ProductService</strong> class manages CRUD operations for products and reviews, handling permissions and roles via the Permit API.</p>
<p>Each operation includes user <code>authorization</code> checks, with appropriate exception handling for Permit API errors and resource not found scenarios.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.boostmytool.store.service;

<span class="hljs-keyword">import</span> com.boostmytool.store.exception.ResourceNotFoundException;
<span class="hljs-keyword">import</span> com.boostmytool.store.model.Product;
<span class="hljs-keyword">import</span> com.boostmytool.store.model.Review;
<span class="hljs-keyword">import</span> io.permit.sdk.Permit;
<span class="hljs-keyword">import</span> io.permit.sdk.api.PermitApiError;
<span class="hljs-keyword">import</span> io.permit.sdk.api.PermitContextError;
<span class="hljs-keyword">import</span> io.permit.sdk.enforcement.Resource;
<span class="hljs-keyword">import</span> io.permit.sdk.enforcement.User;
<span class="hljs-keyword">import</span> io.permit.sdk.openapi.models.RelationshipTupleCreate;
<span class="hljs-keyword">import</span> io.permit.sdk.openapi.models.ResourceInstanceCreate;
<span class="hljs-keyword">import</span> io.permit.sdk.openapi.models.RoleAssignmentCreate;
<span class="hljs-keyword">import</span> org.springframework.stereotype.Service;

<span class="hljs-keyword">import</span> java.io.IOException;
<span class="hljs-keyword">import</span> java.util.ArrayList;
<span class="hljs-keyword">import</span> java.util.HashMap;
<span class="hljs-keyword">import</span> java.util.List;
<span class="hljs-keyword">import</span> java.util.concurrent.atomic.AtomicInteger;

<span class="hljs-meta">@Service</span>  <span class="hljs-comment">// Marks this class as a Spring service</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProductService</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> List&lt;Product&gt; products = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();  <span class="hljs-comment">// In-memory list to store products</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> AtomicInteger productIdCounter = <span class="hljs-keyword">new</span> AtomicInteger();  <span class="hljs-comment">// Counter to generate unique product IDs</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> AtomicInteger reviewIdCounter = <span class="hljs-keyword">new</span> AtomicInteger();   <span class="hljs-comment">// Counter to generate unique review IDs</span>

    <span class="hljs-comment">// Builders for Permit resource instances (product and review)</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Resource.Builder productResourceBuilder = <span class="hljs-keyword">new</span> Resource.Builder(<span class="hljs-string">"product"</span>);
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Resource.Builder reviewResourceBuilder = <span class="hljs-keyword">new</span> Resource.Builder(<span class="hljs-string">"review"</span>);

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> UserService userService;  <span class="hljs-comment">// Service for handling user-related operations</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Permit permit;  <span class="hljs-comment">// Permit SDK instance for handling authorization and resource management</span>

    <span class="hljs-comment">// Constructor for injecting dependencies</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ProductService</span><span class="hljs-params">(UserService userService, Permit permit)</span> </span>{
        <span class="hljs-keyword">this</span>.userService = userService;
        <span class="hljs-keyword">this</span>.permit = permit;
    }

    <span class="hljs-comment">// Method to authorize a user for a given action on a resource</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">authorize</span><span class="hljs-params">(User user, String action, Resource resource)</span> </span>{
        userService.authorize(user, action, resource);
    }

    <span class="hljs-comment">// Authorizes a user to perform an action on a specific product</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">authorize</span><span class="hljs-params">(User user, String action, Product product)</span> </span>{
        <span class="hljs-keyword">var</span> attributes = <span class="hljs-keyword">new</span> HashMap&lt;String, Object&gt;();
        attributes.put(<span class="hljs-string">"vendor"</span>, product.getVendor());  <span class="hljs-comment">// Add vendor attribute to the product</span>
        userService.authorize(user, action, productResourceBuilder.withKey(product.getId().toString()).withAttributes(attributes).build());
    }

    <span class="hljs-comment">// Authorizes a user to perform an action on a specific review</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">authorize</span><span class="hljs-params">(User user, String action, Review review)</span> </span>{
        <span class="hljs-keyword">var</span> attributes = <span class="hljs-keyword">new</span> HashMap&lt;String, Object&gt;();
        attributes.put(<span class="hljs-string">"customer"</span>, review.getCustomer());  <span class="hljs-comment">// Add customer attribute to the review</span>
        userService.authorize(user, action, reviewResourceBuilder.withKey(review.getId().toString()).withAttributes(attributes).build());
    }

    <span class="hljs-comment">// Retrieves a product by its ID, throws an exception if not found</span>
    <span class="hljs-function"><span class="hljs-keyword">private</span> Product <span class="hljs-title">getProductById</span><span class="hljs-params">(<span class="hljs-keyword">int</span> id)</span> </span>{
        <span class="hljs-keyword">return</span> products.stream().filter(product -&gt; product.getId().equals(id))
                .findFirst().orElseThrow(() -&gt; <span class="hljs-keyword">new</span> ResourceNotFoundException(<span class="hljs-string">"Product with id "</span> + id + <span class="hljs-string">" not found"</span>));
    }

    <span class="hljs-comment">// Retrieves all products, checks if the user is authorized to "read" products</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;Product&gt; <span class="hljs-title">getAllProducts</span><span class="hljs-params">(User user)</span> </span>{
        authorize(user, <span class="hljs-string">"read"</span>, productResourceBuilder.build());  <span class="hljs-comment">// User must have "read" permission</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ArrayList&lt;&gt;(products);  <span class="hljs-comment">// Return a copy of the products list</span>
    }

    <span class="hljs-comment">// Retrieves a product by its ID, checks if the user is authorized to "read" the product</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Product <span class="hljs-title">getProduct</span><span class="hljs-params">(User user, <span class="hljs-keyword">int</span> id)</span> </span>{
        authorize(user, <span class="hljs-string">"read"</span>, productResourceBuilder.build());
        <span class="hljs-keyword">return</span> getProductById(id);
    }

    <span class="hljs-comment">// Adds a new product, authorizes the user and creates resource instances and role assignments in Permit</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Product <span class="hljs-title">addProduct</span><span class="hljs-params">(User user, String content)</span> </span>{
        authorize(user, <span class="hljs-string">"create"</span>, productResourceBuilder.build());  <span class="hljs-comment">// Check if user can create a product</span>
        Product product = <span class="hljs-keyword">new</span> Product(productIdCounter.incrementAndGet(), user.getKey(), content);  <span class="hljs-comment">// Create new product</span>

        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// Create resource instance in Permit and assign "vendor" role to the user for this product</span>
            permit.api.resourceInstances.create(<span class="hljs-keyword">new</span> ResourceInstanceCreate(product.getId().toString(), <span class="hljs-string">"product"</span>).withTenant(<span class="hljs-string">"default"</span>));
            permit.api.roleAssignments.assign(<span class="hljs-keyword">new</span> RoleAssignmentCreate(<span class="hljs-string">"vendor"</span>, user.getKey()).withResourceInstance(<span class="hljs-string">"product:"</span> + product.getId()).withTenant(<span class="hljs-string">"default"</span>));
        } <span class="hljs-keyword">catch</span> (IOException | PermitApiError | PermitContextError e) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">"Failed to create resource instance or role assignment: "</span> + e.getMessage());
        }

        products.add(product);  <span class="hljs-comment">// Add product to in-memory list</span>
        <span class="hljs-keyword">return</span> product;
    }

    <span class="hljs-comment">// Updates a product's content, checks if the user is authorized to "update" the product</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Product <span class="hljs-title">updateProduct</span><span class="hljs-params">(User user, <span class="hljs-keyword">int</span> id, String content)</span> </span>{
        Product product = getProductById(id);  <span class="hljs-comment">// Get the product by its ID</span>
        authorize(user, <span class="hljs-string">"update"</span>, product);  <span class="hljs-comment">// Check if user can update the product</span>
        product.setContent(content);  <span class="hljs-comment">// Update product content</span>
        <span class="hljs-keyword">return</span> product;
    }

    <span class="hljs-comment">// Deletes a product, checks if the user is authorized to "delete" the product</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">deleteProduct</span><span class="hljs-params">(User user, <span class="hljs-keyword">int</span> id)</span> </span>{
        <span class="hljs-keyword">boolean</span> isDeleted = products.removeIf(product -&gt; {
            <span class="hljs-keyword">if</span> (product.getId().equals(id)) {
                authorize(user, <span class="hljs-string">"delete"</span>, product);  <span class="hljs-comment">// Check if user can delete the product</span>
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
            }
        });

        <span class="hljs-keyword">if</span> (!isDeleted) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ResourceNotFoundException(<span class="hljs-string">"Product with id "</span> + id + <span class="hljs-string">" not found"</span>);
        }

        <span class="hljs-keyword">try</span> {
            permit.api.resourceInstances.delete(<span class="hljs-string">"product:"</span> + id);  <span class="hljs-comment">// Remove product resource instance from Permit</span>
        } <span class="hljs-keyword">catch</span> (IOException | PermitApiError | PermitContextError e) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(e);
        }
    }

    <span class="hljs-comment">// Adds a review to a product, creates a resource instance and relationship in Permit</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Review <span class="hljs-title">addReview</span><span class="hljs-params">(User user, <span class="hljs-keyword">int</span> productId, String content)</span> </span>{
        authorize(user, <span class="hljs-string">"create"</span>, reviewResourceBuilder.build());  <span class="hljs-comment">// Check if user can create a review</span>
        Product product = getProductById(productId);  <span class="hljs-comment">// Get the product by its ID</span>
        Review review = <span class="hljs-keyword">new</span> Review(reviewIdCounter.incrementAndGet(), user.getKey(), content);  <span class="hljs-comment">// Create new review</span>

        <span class="hljs-keyword">try</span> {
            <span class="hljs-comment">// Create a resource instance for the review and set relationship with the product</span>
            permit.api.resourceInstances.create(<span class="hljs-keyword">new</span> ResourceInstanceCreate(review.getId().toString(), <span class="hljs-string">"review"</span>).withTenant(<span class="hljs-string">"default"</span>));
            permit.api.relationshipTuples.create(<span class="hljs-keyword">new</span> RelationshipTupleCreate(<span class="hljs-string">"product:"</span> + productId, <span class="hljs-string">"parent"</span>, <span class="hljs-string">"review:"</span> + review.getId()));
        } <span class="hljs-keyword">catch</span> (IOException | PermitApiError | PermitContextError e) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(e);
        }

        product.addReview(review);  <span class="hljs-comment">// Add the review to the product</span>
        <span class="hljs-keyword">return</span> review;
    }

    <span class="hljs-comment">// Updates a review's content, checks if the user is authorized to "update" the review</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Review <span class="hljs-title">updateReview</span><span class="hljs-params">(User user, <span class="hljs-keyword">int</span> productId, <span class="hljs-keyword">int</span> reviewId, String content)</span> </span>{
        Product product = getProductById(productId);  <span class="hljs-comment">// Get the product by its ID</span>
        Review review = product.getReviews().stream().filter(c -&gt; c.getId().equals(reviewId))
                .findFirst().orElseThrow(() -&gt; <span class="hljs-keyword">new</span> ResourceNotFoundException(<span class="hljs-string">"Review with id "</span> + reviewId + <span class="hljs-string">" not found"</span>));

        authorize(user, <span class="hljs-string">"update"</span>, review);  <span class="hljs-comment">// Check if user can update the review</span>
        review.setContent(content);  <span class="hljs-comment">// Update review content</span>
        <span class="hljs-keyword">return</span> review;
    }

    <span class="hljs-comment">// Deletes a review, checks if the user is authorized to "delete" the review</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">deleteReview</span><span class="hljs-params">(User user, <span class="hljs-keyword">int</span> productId, <span class="hljs-keyword">int</span> reviewId)</span> </span>{
        Product product = getProductById(productId);  <span class="hljs-comment">// Get the product by its ID</span>
        <span class="hljs-keyword">boolean</span> isDeleted = product.getReviews().removeIf(review -&gt; {
            <span class="hljs-keyword">if</span> (review.getId().equals(reviewId)) {
                authorize(user, <span class="hljs-string">"delete"</span>, review);  <span class="hljs-comment">// Check if user can delete the review</span>
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
            }
        });

        <span class="hljs-keyword">if</span> (!isDeleted) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ResourceNotFoundException(<span class="hljs-string">"Review with id "</span> + reviewId + <span class="hljs-string">" not found"</span>);
        }

        <span class="hljs-keyword">try</span> {
            permit.api.resourceInstances.delete(<span class="hljs-string">"review:"</span> + reviewId);  <span class="hljs-comment">// Remove review resource instance from Permit</span>
        } <span class="hljs-keyword">catch</span> (IOException | PermitApiError | PermitContextError e) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(e);
        }
    }
}
</code></pre>
<p>Then in the code below, the <strong>ProductController</strong> class handles HTTP requests related to products and their reviews. It exposes endpoints for managing products (like <code>creating</code>, <code>updating</code>, <code>deleting</code>, and <code>retrieving</code>) and for managing product reviews.</p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.boostmytool.store.controllers;

<span class="hljs-keyword">import</span> com.boostmytool.store.model.Product;
<span class="hljs-keyword">import</span> com.boostmytool.store.model.Review;
<span class="hljs-keyword">import</span> com.boostmytool.store.service.ProductService;
<span class="hljs-keyword">import</span> io.permit.sdk.enforcement.User;
<span class="hljs-keyword">import</span> jakarta.servlet.http.HttpServletRequest;
<span class="hljs-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;
<span class="hljs-keyword">import</span> org.springframework.http.HttpStatus;
<span class="hljs-keyword">import</span> org.springframework.web.bind.annotation.*;

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

<span class="hljs-meta">@RestController</span>  <span class="hljs-comment">// Indicates that this class is a Spring REST controller</span>
<span class="hljs-meta">@RequestMapping("/api/products")</span>  <span class="hljs-comment">// Base URL for all endpoints in this controller</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProductController</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> ProductService productService;  <span class="hljs-comment">// ProductService instance to handle product-related operations</span>

    <span class="hljs-meta">@Autowired</span>  <span class="hljs-comment">// Autowires ProductService bean automatically</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ProductController</span><span class="hljs-params">(ProductService productService)</span> </span>{
        <span class="hljs-keyword">this</span>.productService = productService;
    }

    <span class="hljs-comment">// GET request to retrieve all products</span>
    <span class="hljs-meta">@GetMapping</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;Product&gt; <span class="hljs-title">getAllProducts</span><span class="hljs-params">(HttpServletRequest request)</span> </span>{
        User currentUser = (User) request.getAttribute(<span class="hljs-string">"user"</span>);  <span class="hljs-comment">// Gets the authenticated user from the request</span>
        <span class="hljs-keyword">return</span> productService.getAllProducts(currentUser);  <span class="hljs-comment">// Calls ProductService to get all products for the user</span>
    }

    <span class="hljs-comment">// GET request to retrieve a product by its ID</span>
    <span class="hljs-meta">@GetMapping("/{id}")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Product <span class="hljs-title">getProductById</span><span class="hljs-params">(HttpServletRequest request, <span class="hljs-meta">@PathVariable("id")</span> <span class="hljs-keyword">int</span> id)</span> </span>{
        User currentUser = (User) request.getAttribute(<span class="hljs-string">"user"</span>);  <span class="hljs-comment">// Gets the authenticated user from the request</span>
        <span class="hljs-keyword">return</span> productService.getProduct(currentUser, id);  <span class="hljs-comment">// Calls ProductService to get the product by ID for the user</span>
    }

    <span class="hljs-comment">// POST request to add a new product</span>
    <span class="hljs-meta">@PostMapping</span>
    <span class="hljs-meta">@ResponseStatus(HttpStatus.CREATED)</span>  <span class="hljs-comment">// Sets the response status to 201 (Created)</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Product <span class="hljs-title">addProduct</span><span class="hljs-params">(HttpServletRequest request, <span class="hljs-meta">@RequestBody</span> String content)</span> </span>{
        User currentUser = (User) request.getAttribute(<span class="hljs-string">"user"</span>);  <span class="hljs-comment">// Gets the authenticated user from the request</span>
        <span class="hljs-keyword">return</span> productService.addProduct(currentUser, content);  <span class="hljs-comment">// Calls ProductService to add a new product</span>
    }

    <span class="hljs-comment">// PUT request to update an existing product by its ID</span>
    <span class="hljs-meta">@PutMapping("/{id}")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Product <span class="hljs-title">updateProduct</span><span class="hljs-params">(HttpServletRequest request, <span class="hljs-meta">@PathVariable("id")</span> <span class="hljs-keyword">int</span> id, <span class="hljs-meta">@RequestBody</span> String content)</span> </span>{
        User currentUser = (User) request.getAttribute(<span class="hljs-string">"user"</span>);  <span class="hljs-comment">// Gets the authenticated user from the request</span>
        <span class="hljs-keyword">return</span> productService.updateProduct(currentUser, id, content);  <span class="hljs-comment">// Calls ProductService to update the product by ID</span>
    }

    <span class="hljs-comment">// DELETE request to delete a product by its ID</span>
    <span class="hljs-meta">@DeleteMapping("/{id}")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">deleteProduct</span><span class="hljs-params">(HttpServletRequest request, <span class="hljs-meta">@PathVariable("id")</span> <span class="hljs-keyword">int</span> id)</span> </span>{
        User currentUser = (User) request.getAttribute(<span class="hljs-string">"user"</span>);  <span class="hljs-comment">// Gets the authenticated user from the request</span>
        productService.deleteProduct(currentUser, id);  <span class="hljs-comment">// Calls ProductService to delete the product by ID</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Deleted product with id "</span> + id;  <span class="hljs-comment">// Returns a success message after deletion</span>
    }

    <span class="hljs-comment">// POST request to add a new review to a product by product ID</span>
    <span class="hljs-meta">@PostMapping("/{id}/review")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Review <span class="hljs-title">addReview</span><span class="hljs-params">(HttpServletRequest request, <span class="hljs-meta">@PathVariable("id")</span> <span class="hljs-keyword">int</span> id, <span class="hljs-meta">@RequestBody</span> String content)</span> </span>{
        User currentUser = (User) request.getAttribute(<span class="hljs-string">"user"</span>);  <span class="hljs-comment">// Gets the authenticated user from the request</span>
        <span class="hljs-keyword">return</span> productService.addReview(currentUser, id, content);  <span class="hljs-comment">// Calls ProductService to add a review to the product</span>
    }

    <span class="hljs-comment">// PUT request to update an existing review by product and review ID</span>
    <span class="hljs-meta">@PutMapping("/{id}/review/{reviewId}")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Review <span class="hljs-title">updateReview</span><span class="hljs-params">(HttpServletRequest request, <span class="hljs-meta">@PathVariable("id")</span> <span class="hljs-keyword">int</span> id, <span class="hljs-meta">@PathVariable("reviewId")</span> <span class="hljs-keyword">int</span> reviewId, <span class="hljs-meta">@RequestBody</span> String content)</span> </span>{
        User currentUser = (User) request.getAttribute(<span class="hljs-string">"user"</span>);  <span class="hljs-comment">// Gets the authenticated user from the request</span>
        <span class="hljs-keyword">return</span> productService.updateReview(currentUser, id, reviewId, content);  <span class="hljs-comment">// Calls ProductService to update the review</span>
    }

    <span class="hljs-comment">// DELETE request to delete a review by product and review ID</span>
    <span class="hljs-meta">@DeleteMapping("/{id}/review/{reviewId}")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">deleteReview</span><span class="hljs-params">(HttpServletRequest request, <span class="hljs-meta">@PathVariable("id")</span> <span class="hljs-keyword">int</span> id, <span class="hljs-meta">@PathVariable("reviewId")</span> <span class="hljs-keyword">int</span> reviewId)</span> </span>{
        User currentUser = (User) request.getAttribute(<span class="hljs-string">"user"</span>);  <span class="hljs-comment">// Gets the authenticated user from the request</span>
        productService.deleteReview(currentUser, id, reviewId);  <span class="hljs-comment">// Calls ProductService to delete the review</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Deleted review with id "</span> + reviewId + <span class="hljs-string">" from product "</span> + id;  <span class="hljs-comment">// Returns a success message after deletion</span>
    }
}
</code></pre>
<h3 id="heading-step-2-get-your-environment-api-key"><strong>Step 2: Get your Environment API Key</strong></h3>
<p>In the UI Dashboard, copy the Environment <code>API Key</code> of the active environment.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729498343969/2bbbd4a0-512f-4b46-a82a-ca41ecb67a4c.png" alt="Permit.io - copy env key" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Then add the env <code>API key</code> and <code>PDP URL</code> in the <code>application.yaml</code> file.</p>
<pre><code class="lang-plaintext">permit:
  pdpUrl: 'http://localhost:7766'
  apiKey: "Your Permit environment API Key"
</code></pre>
<h3 id="heading-step-3-deploy-policy-decision-point-pdp"><strong>Step 3: Deploy Policy Decision Point (PDP)</strong></h3>
<p>The Policy Decision Point (PDP) is deployed in your VPC and is in charge of evaluating your authorization requests. The PDP will ensure zero latency, great performance, high availability, and improved security.</p>
<p>Use the command below to pull the <a target="_blank" href="http://Permit.io">Permit.io</a> PDP container from <code>Docker</code> Hub.</p>
<pre><code class="lang-dockerfile">docker pull permitio/pdp-v2:latest
</code></pre>
<p>Then run the container.</p>
<pre><code class="lang-dockerfile">docker <span class="hljs-keyword">run</span><span class="bash"> -it -p 7766:7000 --env PDP_DEBUG=True --env PDP_API_KEY=&lt;YOUR_API_KEY&gt; permitio/pdp-v2:latest</span>
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729515246656/6bed08e3-6109-4643-a724-f55641d7c974.png" alt="Permit.io - local testing" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-step-4-running-the-app"><strong>Step 4: Running the App</strong></h3>
<p>You can run the application using the following <code>Gradle</code> command:</p>
<pre><code class="lang-dockerfile">./gradlew bootRun
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729515837209/41556843-b8d5-4433-a2f3-93371562d27d.png" alt="Permit.io - how to run the app in local" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h4 id="heading-viewing-and-creating-products"><strong>Viewing and Creating Products</strong></h4>
<p>Let’s now interact with the application endpoints using <a target="_blank" href="https://reqbin.com/curl">REQBIN</a>.</p>
<p>First, create a new user using the <code>/api/users/signup</code> endpoint.</p>
<pre><code class="lang-dockerfile">curl -X POST <span class="hljs-string">"http://localhost:8080/api/users/signup"</span> -H <span class="hljs-string">"Content-Type: application/json"</span> -d <span class="hljs-string">'johndoe'</span>
</code></pre>
<p>You should be able to view the user in your Permit project, under Directory &gt; All Tenants.</p>
<p>Initially, the user has no roles, so it cannot do much. For example, trying to list the products will result in a 403 Forbidden response, as shown below. The 403 error code means the user doesn’t have permissions to access the requested resource, which is products in this case. You can learn more about <a target="_blank" href="https://www.permit.io/blog/401-vs-403-error-whats-the-difference">the difference between 401 and 403 error codes here</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729498632123/aaf26b76-a89f-4e6b-9324-85d082b8061d.png" alt="Permit.io - endpoints" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>For the user to view a list of products, assign them a viewer role using the command below:</p>
<pre><code class="lang-dockerfile">curl -X POST <span class="hljs-string">"http://localhost:8080/api/users/assign-role"</span> \
-H <span class="hljs-string">"Authorization: Bearer johndoe"</span> \
-H <span class="hljs-string">"Content-Type: application/json"</span> \
-d <span class="hljs-string">'viewer'</span>
</code></pre>
<p>You should see that user <code>johndoe</code> was assigned role viewer, as shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729498710784/ebd789fd-ec52-4146-bb94-6f300edb9d7e.png" alt="Permit.io - users" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Since a viewer can create a product, use the command below to create a product with user <code>johndoe</code>.</p>
<pre><code class="lang-dockerfile">curl -X POST <span class="hljs-string">"http://localhost:8080/api/products"</span> -H <span class="hljs-string">"Authorization: Bearer johndoe"</span> -H <span class="hljs-string">"Content-Type: application/json"</span> -d <span class="hljs-string">'MacBook'</span>
</code></pre>
<p>You should see that a new product is created with ID 1 and that the user <code>johndoe</code> has been added as the vendor.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729498758160/c14169f0-d720-465e-9bc1-f7096e6da31a.png" alt="Permit.io - API end points " class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h4 id="heading-adding-reviews-to-products"><strong>Adding Reviews To Products</strong></h4>
<p>To add reviews to products, create another user called <code>jane</code>.</p>
<pre><code class="lang-dockerfile">curl -X POST <span class="hljs-string">"http://localhost:8080/api/users/signup"</span> -H <span class="hljs-string">"Content-Type: application/json"</span> -d <span class="hljs-string">'jane'</span>
</code></pre>
<p>For the user to add a review to products, assign them a <code>viewer</code> role using the command below:</p>
<pre><code class="lang-dockerfile">curl -X POST <span class="hljs-string">"http://localhost:8080/api/users/assign-role"</span> \
-H <span class="hljs-string">"Authorization: Bearer jane"</span> \
-H <span class="hljs-string">"Content-Type: application/json"</span> \
-d <span class="hljs-string">'viewer'</span>
</code></pre>
<p>Then you can add a review to the product added by <code>johndoe</code> using the command below:</p>
<pre><code class="lang-dockerfile">curl -X POST <span class="hljs-string">"http://localhost:8080/api/products/1/review"</span> -H <span class="hljs-string">"Authorization: Bearer jane"</span> -H <span class="hljs-string">"Content-Type: application/json"</span> -d <span class="hljs-string">'The product was in good quality'</span>
</code></pre>
<p>Congratulations! You’ve completed the project for this tutorial.</p>
<h2 id="heading-next-steps">Next Steps</h2>
<p>Now that you've learned how to implement fine-grained authorization in your Java and Spring Boot applications using <a target="_blank" href="http://Permit.io">Permit.io</a>, you might want to explore further.</p>
<p>Here are some valuable resources:</p>
<ul>
<li><p><a target="_blank" href="https://docs.permit.io/">Permit.io docs</a></p>
</li>
<li><p><a target="_blank" href="https://www.permit.io/blog/rbac-vs-abac">RBAC VS ABAC: Choosing the Right Authorization Policy Model</a></p>
</li>
</ul>
<h3 id="heading-before-we-end">Before We End</h3>
<p>I hope you found this tutorial insightful.</p>
<p>Here are some of my other recent blog posts that you might enjoy:</p>
<ul>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/learn-react-key-concepts/">Learn React – A Guide to the Key Concepts</a></p>
</li>
<li><p><a target="_blank" href="https://www.devtoolsacademy.com/blog/neon-vs-supabase"><strong>Neon Postgres vs Supabase</strong></a></p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/nextjs-clerk-neon-fullstack-development/">Full Stack Development with Next.js, Clerk, and Neon Postgres</a></p>
</li>
</ul>
<p>For more tutorials on amazing developer tools, be sure to check out my blog <a target="_blank" href="https://www.devtoolsacademy.com/">DTA</a>.</p>
<p>Follow me on <a target="_blank" href="https://x.com/TheAnkurTyagi">Twitter</a> to get live updates on my other projects.</p>
<p>Happy coding.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How I Built a Custom Video Conferencing App with Stream and Next.js ]]>
                </title>
                <description>
                    <![CDATA[ Building full-stack apps can be tough. You have to think about frontend, APIs, databases, auth – plus you have to know how all of these things work together. And building a project like a video conferencing app from scratch can feel even more overwhe... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-i-built-a-custom-video-conferencing-app-with-stream-and-nextjs/</link>
                <guid isPermaLink="false">66fd86ff9cea0a9dc9177283</guid>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Startups ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webdev ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Developer Tools ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ankur Tyagi ]]>
                </dc:creator>
                <pubDate>Wed, 02 Oct 2024 17:46:39 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1727433361539/498f0742-2ff1-4762-b268-2c25eb22017e.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Building full-stack apps can be tough. You have to think about frontend, APIs, databases, auth – plus you have to know how all of these things work together.</p>
<p>And building a project like a video conferencing app from scratch can feel even more overwhelming, especially with the complexities of managing video streams, user auth, and real-time interactions.</p>
<p>But what if I told you there’s an easier way to do this – one that lets you build your video conferencing app in a fraction of the time?</p>
<p>In this article, I’ll show you how I built a video conferencing app using <a target="_blank" href="https://getstream.io/">Stream</a> and Clerk in Next.js.</p>
<p>Here is the <a target="_blank" href="https://github.com/tyaga001/facetime-on-stream">source code</a> (remember to give it a star ⭐).</p>
<p>Before we start, let me tell you why I wrote this tutorial.</p>
<p>I’m a Software Engineer who cares about writing and I <strong>love</strong> to <strong>code</strong>, <strong>design</strong>, <strong>develop</strong>, and then <strong>teach</strong> people.</p>
<p>I've been using open-source projects, products, and services for a while now, and contributing to many of them to improve them how I can. Last month I built an open-source blog for “awesome developer tools“ called - <a target="_blank" href="https://www.devtoolsacademy.com/">devtoolsacademy</a></p>
<p><a target="_blank" href="https://www.devtoolsacademy.com/"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1727430858395/70ffbec4-69ab-4f31-a9cb-02b44066ac6b.png" alt="devtoolsacademy" class="image--center mx-auto" width="3072" height="1830" loading="lazy"></a></p>
<p>This article is about sharing the experience I’ve had using yet another awesome developer tool.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents:</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-stream">What is Stream?</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-build-the-app-interface-with-nextjs">How to Build the App Interface with Next.js</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-the-create-link-modal">The Create Link Modal</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-instant-meeting-modal">The Instant Meeting Modal</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-join-meeting-modal">The Join Meeting Modal</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-authenticate-users-with-clerk">How to Authenticate Users with Clerk</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-stream-in-a-nextjs-app">How to Set Up Stream in a Next.js app</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-and-join-calls-with-stream">How to Create and Join Calls with Stream</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-creating-and-scheduling-calls">Creating and Scheduling calls</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-joining-calls-and-the-meeting-page">Joining calls and the Meeting Page</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-retrieving-upcoming-calls">Retrieving Upcoming Calls</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-what-is-stream">What is Stream?</h2>
<p><a target="_blank" href="https://getstream.io/">Stream</a> is an open-source cloud-based platform that provides APIs and SDKs for building scalable and feature-rich real-time applications. It offers pre-built UI components for creating enterprise-grade software apps with features like chat, video, audio, and activity feeds.</p>
<p><a target="_blank" href="https://getstream.io"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726475007023/be45aa40-7794-434a-8f5d-f4b637d97fd8.png" alt="What is Stream" class="image--center mx-auto" width="3114" height="1778" loading="lazy"></a></p>
<p>Here's how I'll use <code>Stream</code> while building the app:</p>
<ul>
<li><p>Set up real-time video and audio calls</p>
</li>
<li><p>Use Stream's UI components to quickly build the interface</p>
</li>
<li><p>Implement key features like <code>video</code> and <code>audio</code> calls</p>
</li>
<li><p><code>Call Types</code> – I'll implement instant meetings and pre-scheduled calls using Stream</p>
</li>
<li><p>Leverage Stream's call and participant objects to manage <code>call state</code></p>
</li>
</ul>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>To fully understand the tutorial, you need to have a basic understanding of <a target="_blank" href="https://www.freecodecamp.org/news/learn-react-key-concepts/">React</a> and <a target="_blank" href="https://theankurtyagi.com/next-js/">Next.js</a>. You’ll also need the following:</p>
<ul>
<li><p><a target="_blank" href="https://getstream.io/chat/docs/sdk/react/">Stream React SDK</a> - provides pre-built UI components for adding video call features quickly.</p>
</li>
<li><p><a target="_blank" href="https://github.com/GetStream/stream-node">Stream Node.js SDK</a> - for managing server-side interactions and keeping Stream's state in sync.</p>
</li>
<li><p><a target="_blank" href="https://clerk.com/">Clerk</a> - a comprehensive user management platform to handle authentication effortlessly.</p>
</li>
<li><p><a target="_blank" href="https://headlessui.com/">Headless UI</a> - provides accessible UI components for building user-friendly applications.</p>
</li>
<li><p><a target="_blank" href="https://www.npmjs.com/package/react-copy-to-clipboard">React Copy-to-Clipboard</a> - allows users to easily copy meeting links within the app.</p>
</li>
<li><p><a target="_blank" href="https://react-icons.github.io/react-icons/">React Icons</a> - offers a library of easily integrated icons.</p>
</li>
</ul>
<h2 id="heading-how-to-build-the-app-interface-with-nextjs">How to Build the App Interface with Next.js</h2>
<p>In this section, I'll guide you through creating the user interface for the video-conferencing app. The interface will allow users to easily create, join, and schedule meetings, as well as view their upcoming meetings.</p>
<p>First, let’s create a Next.js TypeScript project by running the code snippet below:</p>
<pre><code class="lang-bash">npx create-next-app facetime-app
</code></pre>
<p>Then install the following packages:</p>
<ul>
<li><p><a target="_blank" href="https://react-icons.github.io/react-icons/">React icons</a> - a popular React icons package</p>
</li>
<li><p><a target="_blank" href="https://headlessui.com/">Headless UI</a> - provides a set of accessible UI components</p>
</li>
<li><p><a target="_blank" href="https://www.npmjs.com/package/react-copy-to-clipboard">React-copy-to-clipboard</a> - a lightweight package that enables us to copy meeting links.</p>
</li>
</ul>
<pre><code class="lang-bash">npm install react-icons @headlessui/react react-copy-to-clipboard
</code></pre>
<p>Copy the code snippet below into the <code>app/page.tsx</code> file:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { FaLink, FaVideo } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-icons/fa"</span>;
<span class="hljs-keyword">import</span> InstantMeeting <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/modals/InstantMeeting"</span>;
<span class="hljs-keyword">import</span> UpcomingMeeting <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/modals/UpcomingMeeting"</span>;
<span class="hljs-keyword">import</span> CreateLink <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/modals/CreateLink"</span>;
<span class="hljs-keyword">import</span> JoinMeeting <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/modals/JoinMeeting"</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">Dashboard</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [startInstantMeeting, setStartInstantMeeting] =
        useState&lt;<span class="hljs-built_in">boolean</span>&gt;(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> [joinMeeting, setJoinMeeting] = useState&lt;<span class="hljs-built_in">boolean</span>&gt;(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> [showUpcomingMeetings, setShowUpcomingMeetings] =
        useState&lt;<span class="hljs-built_in">boolean</span>&gt;(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> [showCreateLink, setShowCreateLink] = useState&lt;<span class="hljs-built_in">boolean</span>&gt;(<span class="hljs-literal">false</span>);

    <span class="hljs-keyword">return</span> (
        &lt;&gt;
            &lt;button
                className=<span class="hljs-string">' top-5 right-5 text-sm fixed bg-green-500 px-2 w-[150px] hover:bg-green-600 py-3 flex flex-col items-center text-white rounded-md shadow-sm cursor-pointer z-10'</span>
                onClick={<span class="hljs-function">() =&gt;</span> setJoinMeeting(<span class="hljs-literal">true</span>)}
            &gt;
                &lt;FaVideo className=<span class="hljs-string">'mb-[3px] text-white'</span> /&gt;
                Join FaceTime
            &lt;/button&gt;

            &lt;main className=<span class="hljs-string">'w-full h-screen flex flex-col items-center justify-center'</span>&gt;
                &lt;h1 className=<span class="hljs-string">'font-bold text-2xl text-center'</span>&gt;FaceTime&lt;/h1&gt;
                &lt;div className=<span class="hljs-string">'flex flex-col'</span>&gt;
                    &lt;button
                        className=<span class="hljs-string">'text-green-500 underline text-sm text-center cursor-pointer'</span>
                        onClick={<span class="hljs-function">() =&gt;</span> setShowUpcomingMeetings(<span class="hljs-literal">true</span>)}
                    &gt;
                        Upcoming FaceTime
                    &lt;/button&gt;
                &lt;/div&gt;

                &lt;div className=<span class="hljs-string">'flex items-center justify-center space-x-4 mt-6'</span>&gt;
                    &lt;button
                        className=<span class="hljs-string">'bg-gray-500 px-4 w-[200px] py-3 flex flex-col items-center hover:bg-gray-600 text-white rounded-md shadow-sm'</span>
                        onClick={<span class="hljs-function">() =&gt;</span> setShowCreateLink(<span class="hljs-literal">true</span>)}
                    &gt;
                        &lt;FaLink className=<span class="hljs-string">'mb-[3px] text-gray-300'</span> /&gt;
                        Create link
                    &lt;/button&gt;
                    &lt;button
                        className=<span class="hljs-string">'bg-green-500 px-4 w-[200px] hover:bg-green-600 py-3 flex flex-col items-center text-white rounded-md shadow-sm'</span>
                        onClick={<span class="hljs-function">() =&gt;</span> setStartInstantMeeting(<span class="hljs-literal">true</span>)}
                    &gt;
                        &lt;FaVideo className=<span class="hljs-string">'mb-[3px] text-white'</span> /&gt;
                        New FaceTime
                    &lt;/button&gt;
                &lt;/div&gt;
            &lt;/main&gt;

            {startInstantMeeting &amp;&amp; (
                &lt;InstantMeeting
                    enable={startInstantMeeting}
                    setEnable={setStartInstantMeeting}
                /&gt;
            )}
            {showUpcomingMeetings &amp;&amp; (
                &lt;UpcomingMeeting
                    enable={showUpcomingMeetings}
                    setEnable={setShowUpcomingMeetings}
                /&gt;
            )}
            {showCreateLink &amp;&amp; (
                &lt;CreateLink enable={showCreateLink} setEnable={setShowCreateLink} /&gt;
            )}
            {joinMeeting &amp;&amp; (
                &lt;JoinMeeting enable={joinMeeting} setEnable={setJoinMeeting} /&gt;
            )}
        &lt;/&gt;
    );
}
</code></pre>
<p>The code snippet above renders multiple buttons that allow users to perform actions like joining, creating, and scheduling a call. Each button opens a modal that prompts the user to provide additional details specific to the action they are performing.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726481911712/286f7349-0d95-419d-97e5-193371307e13.png" alt="facetime-app-home-page" class="image--center mx-auto" width="3110" height="1818" loading="lazy"></p>
<p>Next, let’s create a <code>modals</code> folder within the Next.js app directory and add the following components to the <code>modals</code> folder:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> app
mkdir modals &amp;&amp; <span class="hljs-built_in">cd</span> modals
touch CreateLink.tsx InstantMeeting.tsx JoinMeeting.tsx UpcomingMeeting.tsx
</code></pre>
<p>The <code>CreateLink</code> modal allows users to provide a description and schedule a time for the call. The <code>InstantMeeting</code> modal lets users start an instant meeting by providing a call description. The <code>JoinMeeting</code> modal enables users to enter a call link and join a meeting. And the <code>UpcomingMeeting</code> modal displays all scheduled upcoming calls.</p>
<h3 id="heading-the-create-link-modal">The Create Link Modal</h3>
<p>Copy the code snippet below into the <code>CreateLink</code> modal:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> {
    Dialog,
    DialogTitle,
    DialogPanel,
    Transition,
    Description,
    TransitionChild,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@headlessui/react"</span>;
<span class="hljs-keyword">import</span> { Fragment, SetStateAction, useState, Dispatch } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> CopyToClipboard <span class="hljs-keyword">from</span> <span class="hljs-string">"react-copy-to-clipboard"</span>;
<span class="hljs-keyword">import</span> { FaCopy } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-icons/fa"</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">CreateLink</span>(<span class="hljs-params">{ enable, setEnable }: Props</span>) </span>{
    <span class="hljs-keyword">const</span> [showMeetingLink, setShowMeetingLink] = useState(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> [facetimeLink, setFacetimeLink] = useState&lt;<span class="hljs-built_in">string</span>&gt;(<span class="hljs-string">""</span>);
    <span class="hljs-keyword">const</span> closeModal = <span class="hljs-function">() =&gt;</span> setEnable(<span class="hljs-literal">false</span>);

    <span class="hljs-keyword">return</span> (
        &lt;&gt;
            &lt;Transition appear show={enable} <span class="hljs-keyword">as</span>={Fragment}&gt;
                &lt;Dialog <span class="hljs-keyword">as</span>=<span class="hljs-string">'div'</span> className=<span class="hljs-string">'relative z-10'</span> onClose={closeModal}&gt;
                    &lt;TransitionChild
                        <span class="hljs-keyword">as</span>={Fragment}
                        enter=<span class="hljs-string">'ease-out duration-300'</span>
                        enterFrom=<span class="hljs-string">'opacity-0'</span>
                        enterTo=<span class="hljs-string">'opacity-100'</span>
                        leave=<span class="hljs-string">'ease-in duration-200'</span>
                        leaveFrom=<span class="hljs-string">'opacity-100'</span>
                        leaveTo=<span class="hljs-string">'opacity-0'</span>
                    &gt;
                        &lt;div className=<span class="hljs-string">'fixed inset-0 bg-black/75'</span> /&gt;
                    &lt;/TransitionChild&gt;

                    &lt;div className=<span class="hljs-string">'fixed inset-0 overflow-y-auto'</span>&gt;
                        &lt;div className=<span class="hljs-string">'flex min-h-full items-center justify-center p-4 text-center'</span>&gt;
                            &lt;TransitionChild
                                <span class="hljs-keyword">as</span>={Fragment}
                                enter=<span class="hljs-string">'ease-out duration-300'</span>
                                enterFrom=<span class="hljs-string">'opacity-0 scale-95'</span>
                                enterTo=<span class="hljs-string">'opacity-100 scale-100'</span>
                                leave=<span class="hljs-string">'ease-in duration-200'</span>
                                leaveFrom=<span class="hljs-string">'opacity-100 scale-100'</span>
                                leaveTo=<span class="hljs-string">'opacity-0 scale-95'</span>
                            &gt;
                                &lt;DialogPanel className=<span class="hljs-string">'w-full max-w-2xl transform overflow-hidden rounded-2xl bg-white p-6 align-middle shadow-xl transition-all text-center'</span>&gt;
                                    {showMeetingLink ? (
                                        &lt;MeetingLink facetimeLink={facetimeLink} /&gt;
                                    ) : (
                                        &lt;MeetingForm
                                            setShowMeetingLink={setShowMeetingLink}
                                            setFacetimeLink={setFacetimeLink}
                                        /&gt;
                                    )}
                                &lt;/DialogPanel&gt;
                            &lt;/TransitionChild&gt;
                        &lt;/div&gt;
                    &lt;/div&gt;
                &lt;/Dialog&gt;
            &lt;/Transition&gt;
        &lt;/&gt;
    );
}
</code></pre>
<p>The code snippet above renders a form that allows users to input a description and select a time to schedule a call. Once the call is created, the generated link is displayed and can be copied.</p>
<p>Finally, add the <code>MeetingForm</code> and <code>MeetingLink</code> components below the <code>CreateLink</code> component:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> MeetingForm = <span class="hljs-function">(<span class="hljs-params">{
    setShowMeetingLink,
    setFacetimeLink,
}: {
    setShowMeetingLink: React.Dispatch&lt;SetStateAction&lt;<span class="hljs-built_in">boolean</span>&gt;&gt;;
    setFacetimeLink: Dispatch&lt;SetStateAction&lt;<span class="hljs-built_in">string</span>&gt;&gt;;
}</span>) =&gt;</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> handleStartMeeting = <span class="hljs-keyword">async</span> (e: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {
        e.preventDefault();
        <span class="hljs-built_in">console</span>.log({ description, dateTime });
    };

    <span class="hljs-keyword">return</span> (
        &lt;&gt;
            &lt;DialogTitle
                <span class="hljs-keyword">as</span>=<span class="hljs-string">'h3'</span>
                className=<span class="hljs-string">'text-lg font-bold leading-6 text-green-600'</span>
            &gt;
                Schedule a FaceTime
            &lt;/DialogTitle&gt;

            &lt;Description className=<span class="hljs-string">'text-xs opacity-40 mb-4'</span>&gt;
                Schedule a FaceTime meeting <span class="hljs-keyword">with</span> your cliq
            &lt;/Description&gt;

            &lt;form className=<span class="hljs-string">'w-full'</span> onSubmit={handleStartMeeting}&gt;
                &lt;label
                    className=<span class="hljs-string">'block text-left text-sm font-medium text-gray-700'</span>
                    htmlFor=<span class="hljs-string">'description'</span>
                &gt;
                    Meeting Description
                &lt;/label&gt;
                &lt;input
                    <span class="hljs-keyword">type</span>=<span class="hljs-string">'text'</span>
                    name=<span class="hljs-string">'description'</span>
                    id=<span class="hljs-string">'description'</span>
                    value={description}
                    onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setDescription(e.target.value)}
                    className=<span class="hljs-string">'mt-1 block w-full text-sm py-3 px-4 border-gray-200 border-[1px] rounded mb-3'</span>
                    required
                    placeholder=<span class="hljs-string">'Enter a description for the meeting'</span>
                /&gt;

                &lt;label
                    className=<span class="hljs-string">'block text-left text-sm font-medium text-gray-700'</span>
                    htmlFor=<span class="hljs-string">'date'</span>
                &gt;
                    <span class="hljs-built_in">Date</span> and Time
                &lt;/label&gt;

                &lt;input
                    <span class="hljs-keyword">type</span>=<span class="hljs-string">'datetime-local'</span>
                    id=<span class="hljs-string">'date'</span>
                    name=<span class="hljs-string">'date'</span>
                    required
                    className=<span class="hljs-string">'mt-1 block w-full text-sm py-3 px-4 border-gray-200 border-[1px] rounded mb-3'</span>
                    value={dateTime}
                    onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setDateTime(e.target.value)}
                /&gt;

                &lt;button className=<span class="hljs-string">'w-full bg-green-600 text-white py-3 rounded mt-4'</span>&gt;
                    Create FaceTime
                &lt;/button&gt;
            &lt;/form&gt;
        &lt;/&gt;
    );
};
</code></pre>
<p>The <code>MeetingForm</code> component accepts the call description and scheduled time, while the <code>MeetingLink</code> component displays the generated call link and allows users to copy it.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> MeetingLink = <span class="hljs-function">(<span class="hljs-params">{ facetimeLink }: { facetimeLink: <span class="hljs-built_in">string</span> }</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [copied, setCopied] = useState&lt;<span class="hljs-built_in">boolean</span>&gt;(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> handleCopy = <span class="hljs-function">() =&gt;</span> setCopied(<span class="hljs-literal">true</span>);

    <span class="hljs-keyword">return</span> (
        &lt;&gt;
            &lt;DialogTitle
                <span class="hljs-keyword">as</span>=<span class="hljs-string">'h3'</span>
                className=<span class="hljs-string">'text-lg font-bold leading-6 text-green-600'</span>
            &gt;
                Copy FaceTime Link
            &lt;/DialogTitle&gt;

            &lt;Description className=<span class="hljs-string">'text-xs opacity-40 mb-4'</span>&gt;
                You can share the facetime link <span class="hljs-keyword">with</span> your participants
            &lt;/Description&gt;

            &lt;div className=<span class="hljs-string">'bg-gray-100 p-4 rounded flex items-center justify-between'</span>&gt;
                &lt;p className=<span class="hljs-string">'text-xs text-gray-500'</span>&gt;
                    {<span class="hljs-string">`<span class="hljs-subst">${process.env.NEXT_PUBLIC_FACETIME_HOST}</span>/<span class="hljs-subst">${facetimeLink}</span>`</span>}
                &lt;/p&gt;

                &lt;CopyToClipboard
                    onCopy={handleCopy}
                    text={<span class="hljs-string">`<span class="hljs-subst">${process.env.NEXT_PUBLIC_FACETIME_HOST}</span>/<span class="hljs-subst">${facetimeLink}</span>`</span>}
                &gt;
                    &lt;FaCopy className=<span class="hljs-string">'text-green-600 text-lg cursor-pointer'</span> /&gt;
                &lt;/CopyToClipboard&gt;
            &lt;/div&gt;

            {copied &amp;&amp; (
                &lt;p className=<span class="hljs-string">'text-red-600 text-xs mt-2'</span>&gt;Link copied to clipboard&lt;/p&gt;
            )}
        &lt;/&gt;
    );
};
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726482044698/0cb22caa-3e5a-4f01-9fa2-25c7ce77b08a.png" alt="facetime-app-schedule-popup" class="image--center mx-auto" width="3098" height="1828" loading="lazy"></p>
<h3 id="heading-the-instant-meeting-modal">The Instant Meeting Modal</h3>
<p>Copy the following code snippet into the <code>InstantMeeting</code> modal:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> {
    Dialog,
    DialogTitle,
    DialogPanel,
    Transition,
    Description,
    TransitionChild,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@headlessui/react"</span>;
<span class="hljs-keyword">import</span> { FaCopy } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-icons/fa"</span>;
<span class="hljs-keyword">import</span> CopyToClipboard <span class="hljs-keyword">from</span> <span class="hljs-string">"react-copy-to-clipboard"</span>;
<span class="hljs-keyword">import</span> { Fragment, useState, Dispatch, SetStateAction } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useStreamVideoClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@stream-io/video-react-sdk"</span>;
<span class="hljs-keyword">import</span> { useUser } <span class="hljs-keyword">from</span> <span class="hljs-string">"@clerk/nextjs"</span>;
<span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">"next/link"</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">InstantMeeting</span>(<span class="hljs-params">{ enable, setEnable }: Props</span>) </span>{
    <span class="hljs-keyword">const</span> [showMeetingLink, setShowMeetingLink] = useState(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">const</span> [facetimeLink, setFacetimeLink] = useState&lt;<span class="hljs-built_in">string</span>&gt;(<span class="hljs-string">""</span>);

    <span class="hljs-keyword">const</span> closeModal = <span class="hljs-function">() =&gt;</span> setEnable(<span class="hljs-literal">false</span>);

    <span class="hljs-keyword">return</span> (
        &lt;&gt;
            &lt;Transition appear show={enable} <span class="hljs-keyword">as</span>={Fragment}&gt;
                &lt;Dialog <span class="hljs-keyword">as</span>=<span class="hljs-string">'div'</span> className=<span class="hljs-string">'relative z-10'</span> onClose={closeModal}&gt;
                    &lt;TransitionChild
                        <span class="hljs-keyword">as</span>={Fragment}
                        enter=<span class="hljs-string">'ease-out duration-300'</span>
                        enterFrom=<span class="hljs-string">'opacity-0'</span>
                        enterTo=<span class="hljs-string">'opacity-100'</span>
                        leave=<span class="hljs-string">'ease-in duration-200'</span>
                        leaveFrom=<span class="hljs-string">'opacity-100'</span>
                        leaveTo=<span class="hljs-string">'opacity-0'</span>
                    &gt;
                        &lt;div className=<span class="hljs-string">'fixed inset-0 bg-black/75'</span> /&gt;
                    &lt;/TransitionChild&gt;

                    &lt;div className=<span class="hljs-string">'fixed inset-0 overflow-y-auto'</span>&gt;
                        &lt;div className=<span class="hljs-string">'flex min-h-full items-center justify-center p-4 text-center'</span>&gt;
                            &lt;TransitionChild
                                <span class="hljs-keyword">as</span>={Fragment}
                                enter=<span class="hljs-string">'ease-out duration-300'</span>
                                enterFrom=<span class="hljs-string">'opacity-0 scale-95'</span>
                                enterTo=<span class="hljs-string">'opacity-100 scale-100'</span>
                                leave=<span class="hljs-string">'ease-in duration-200'</span>
                                leaveFrom=<span class="hljs-string">'opacity-100 scale-100'</span>
                                leaveTo=<span class="hljs-string">'opacity-0 scale-95'</span>
                            &gt;
                                &lt;DialogPanel className=<span class="hljs-string">'w-full max-w-2xl transform overflow-hidden rounded-2xl bg-white p-6 align-middle shadow-xl transition-all text-center'</span>&gt;
                                    {showMeetingLink ? (
                                        &lt;MeetingLink facetimeLink={facetimeLink} /&gt;
                                    ) : (
                                        &lt;MeetingForm
                                            setShowMeetingLink={setShowMeetingLink}
                                            setFacetimeLink={setFacetimeLink}
                                        /&gt;
                                    )}
                                &lt;/DialogPanel&gt;
                            &lt;/TransitionChild&gt;
                        &lt;/div&gt;
                    &lt;/div&gt;
                &lt;/Dialog&gt;
            &lt;/Transition&gt;
        &lt;/&gt;
    );
}
</code></pre>
<p>The code snippet above renders a form that allows users to provide a call description. Once the call is created, the link is generated and available to be copied before starting the call.</p>
<p>Finally, add the <code>MeetingForm</code> and <code>MeetingLink</code> components below the <code>CreateLink</code> component:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> MeetingForm = <span class="hljs-function">(<span class="hljs-params">{
    setShowMeetingLink,
    setFacetimeLink,
}: {
    setShowMeetingLink: Dispatch&lt;SetStateAction&lt;<span class="hljs-built_in">boolean</span>&gt;&gt;;
    setFacetimeLink: Dispatch&lt;SetStateAction&lt;<span class="hljs-built_in">string</span>&gt;&gt;;
}</span>) =&gt;</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> handleStartMeeting = <span class="hljs-keyword">async</span> (e: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {
        e.preventDefault();
        <span class="hljs-built_in">console</span>.log({ description });
    };

    <span class="hljs-keyword">return</span> (
        &lt;&gt;
            &lt;DialogTitle
                <span class="hljs-keyword">as</span>=<span class="hljs-string">'h3'</span>
                className=<span class="hljs-string">'text-lg font-bold leading-6 text-green-600'</span>
            &gt;
                Create Instant FaceTime
            &lt;/DialogTitle&gt;

            &lt;Description className=<span class="hljs-string">'text-xs opacity-40 mb-4'</span>&gt;
                You can start a <span class="hljs-keyword">new</span> FaceTime instantly.
            &lt;/Description&gt;

            &lt;form className=<span class="hljs-string">'w-full'</span> onSubmit={handleStartMeeting}&gt;
                &lt;label
                    className=<span class="hljs-string">'block text-left text-sm font-medium text-gray-700'</span>
                    htmlFor=<span class="hljs-string">'description'</span>
                &gt;
                    Meeting Description
                &lt;/label&gt;
                &lt;input
                    <span class="hljs-keyword">type</span>=<span class="hljs-string">'text'</span>
                    name=<span class="hljs-string">'description'</span>
                    id=<span class="hljs-string">'description'</span>
                    value={description}
                    required
                    onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setDescription(e.target.value)}
                    className=<span class="hljs-string">'mt-1 block w-full text-sm py-3 px-4 border-gray-200 border-[1px] rounded mb-3'</span>
                    placeholder=<span class="hljs-string">'Enter a description for the meeting'</span>
                /&gt;

                &lt;button className=<span class="hljs-string">'w-full bg-green-600 text-white py-3 rounded mt-4'</span>&gt;
                    Proceed
                &lt;/button&gt;
            &lt;/form&gt;
        &lt;/&gt;
    );
};
</code></pre>
<p>The <code>MeetingForm</code> component accepts the call description, while the <code>MeetingLink</code> component displays the generated call link and allows users to copy it before starting the call.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726482110082/638609aa-e0ae-4cc4-b520-2050966180b4.png" alt="facetime-app-create-instant-facetime" class="image--center mx-auto" width="3098" height="1792" loading="lazy"></p>
<h3 id="heading-the-join-meeting-modal">The Join Meeting Modal</h3>
<p>Copy the code snippet below into the <code>JoinMeeting.tsx</code> file. It renders a form that accepts the call link and redirects users to the call page.</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> {
    Dialog,
    DialogTitle,
    DialogPanel,
    Transition,
    TransitionChild,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@headlessui/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> { Fragment, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</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">JoinMeeting</span>(<span class="hljs-params">{ enable, setEnable }: Props</span>) </span>{
    <span class="hljs-keyword">const</span> closeModal = <span class="hljs-function">() =&gt;</span> setEnable(<span class="hljs-literal">false</span>);

    <span class="hljs-keyword">return</span> (
        &lt;&gt;
            &lt;Transition appear show={enable} <span class="hljs-keyword">as</span>={Fragment}&gt;
                &lt;Dialog <span class="hljs-keyword">as</span>=<span class="hljs-string">'div'</span> className=<span class="hljs-string">'relative z-10'</span> onClose={closeModal}&gt;
                    &lt;TransitionChild
                        <span class="hljs-keyword">as</span>={Fragment}
                        enter=<span class="hljs-string">'ease-out duration-300'</span>
                        enterFrom=<span class="hljs-string">'opacity-0'</span>
                        enterTo=<span class="hljs-string">'opacity-100'</span>
                        leave=<span class="hljs-string">'ease-in duration-200'</span>
                        leaveFrom=<span class="hljs-string">'opacity-100'</span>
                        leaveTo=<span class="hljs-string">'opacity-0'</span>
                    &gt;
                        &lt;div className=<span class="hljs-string">'fixed inset-0 bg-black/75'</span> /&gt;
                    &lt;/TransitionChild&gt;

                    &lt;div className=<span class="hljs-string">'fixed inset-0 overflow-y-auto'</span>&gt;
                        &lt;div className=<span class="hljs-string">'flex min-h-full items-center justify-center p-4 text-center'</span>&gt;
                            &lt;TransitionChild
                                <span class="hljs-keyword">as</span>={Fragment}
                                enter=<span class="hljs-string">'ease-out duration-300'</span>
                                enterFrom=<span class="hljs-string">'opacity-0 scale-95'</span>
                                enterTo=<span class="hljs-string">'opacity-100 scale-100'</span>
                                leave=<span class="hljs-string">'ease-in duration-200'</span>
                                leaveFrom=<span class="hljs-string">'opacity-100 scale-100'</span>
                                leaveTo=<span class="hljs-string">'opacity-0 scale-95'</span>
                            &gt;
                                &lt;DialogPanel className=<span class="hljs-string">'w-full max-w-2xl transform overflow-hidden rounded-2xl bg-white p-6 align-middle shadow-xl transition-all text-center'</span>&gt;
                                    &lt;CallLinkForm /&gt;
                                &lt;/DialogPanel&gt;
                            &lt;/TransitionChild&gt;
                        &lt;/div&gt;
                    &lt;/div&gt;
                &lt;/Dialog&gt;
            &lt;/Transition&gt;
        &lt;/&gt;
    );
}
</code></pre>
<p>Add the <code>CallLinkForm</code> below the <code>JoinMeeting</code> component:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> CallLinkForm = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [link, setLink] = useState&lt;<span class="hljs-built_in">string</span>&gt;(<span class="hljs-string">""</span>);
    <span class="hljs-keyword">const</span> router = useRouter();

    <span class="hljs-keyword">const</span> handleJoinMeeting = <span class="hljs-function">(<span class="hljs-params">e: React.FormEvent&lt;HTMLFormElement&gt;</span>) =&gt;</span> {
        e.preventDefault();
        router.push(<span class="hljs-string">`<span class="hljs-subst">${link}</span>`</span>);
    };

    <span class="hljs-keyword">return</span> (
        &lt;&gt;
            &lt;DialogTitle
                <span class="hljs-keyword">as</span>=<span class="hljs-string">'h3'</span>
                className=<span class="hljs-string">'text-lg font-bold leading-6 text-green-600'</span>
            &gt;
                Join FaceTime
            &lt;/DialogTitle&gt;

            &lt;form className=<span class="hljs-string">'w-full'</span> onSubmit={handleJoinMeeting}&gt;
                &lt;label
                    className=<span class="hljs-string">'block text-left text-sm font-medium text-gray-700'</span>
                    htmlFor=<span class="hljs-string">'link'</span>
                &gt;
                    Enter the FaceTime link
                &lt;/label&gt;
                &lt;input
                    <span class="hljs-keyword">type</span>=<span class="hljs-string">'url'</span>
                    name=<span class="hljs-string">'link'</span>
                    id=<span class="hljs-string">'link'</span>
                    value={link}
                    onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setLink(e.target.value)}
                    className=<span class="hljs-string">'mt-1 block w-full text-sm py-3 px-4 border-gray-200 border-[1px] rounded mb-3'</span>
                    placeholder=<span class="hljs-string">'Enter the FaceTime link'</span>
                /&gt;

                &lt;button className=<span class="hljs-string">'w-full bg-green-600 text-white py-3 rounded mt-4'</span>&gt;
                    Join now
                &lt;/button&gt;
            &lt;/form&gt;
        &lt;/&gt;
    );
};
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726482173301/09881faa-54f8-4293-a186-b608ef5a0e05.png" alt="facetime-app-join-popup" class="image--center mx-auto" width="3104" height="1788" loading="lazy"></p>
<p>Congratulations! You’ve completed the app’s interface.</p>
<h2 id="heading-how-to-authenticate-users-with-clerk">How to Authenticate Users with Clerk</h2>
<p><a target="_blank" href="https://clerk.com/">Clerk</a> is a user management platform that enables you to add auth to web apps.</p>
<p>You can install the <a target="_blank" href="https://clerk.com/docs/quickstarts/nextjs">Clerk Next.js SDK</a> by running the following code snippet in your terminal:</p>
<pre><code class="lang-bash">npm install @clerk/nextjs
</code></pre>
<p>Create a <code>middleware.ts</code> file within the Next.js <code>src</code> folder and copy the code snippet below into the file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { clerkMiddleware, createRouteMatcher } <span class="hljs-keyword">from</span> <span class="hljs-string">"@clerk/nextjs/server"</span>;

<span class="hljs-keyword">const</span> protectedRoutes = createRouteMatcher([
    <span class="hljs-string">"/facetime(.*)"</span>,
    <span class="hljs-string">"/dashboard"</span>,
    <span class="hljs-string">"/"</span>,
]);

<span class="hljs-comment">//👇🏻 protects the route</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> clerkMiddleware(<span class="hljs-function">(<span class="hljs-params">auth, req</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (protectedRoutes(req)) {
        auth().protect();
    }
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> config = {
    matcher: [<span class="hljs-string">"/((?!.*\\\\..*|_next).*)"</span>, <span class="hljs-string">"/"</span>, <span class="hljs-string">"/(api|trpc)(.*)"</span>],
};
</code></pre>
<p>The <code>createRouteMatcher</code> function accepts an array containing routes to be protected from unauthenticated users and the <code>clerkMiddleware()</code> function ensures the routes are protected.</p>
<p>Next, import the following Clerk components into the <code>app/layout.tsx</code> file and update the <code>RootLayout</code> function as shown below:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> {
    ClerkProvider,
    SignInButton,
    SignedIn,
    SignedOut,
    UserButton,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@clerk/nextjs"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./globals.css"</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">RootLayout</span>(<span class="hljs-params">{
    children,
}: {
    children: React.ReactNode;
}</span>) </span>{
    <span class="hljs-keyword">return</span> (
        &lt;ClerkProvider&gt;
            &lt;html lang=<span class="hljs-string">'en'</span>&gt;
                &lt;body className={inter.className}&gt;
                    &lt;nav className=<span class="hljs-string">'w-full py-4 md:px-8 px-4 text-center flex items-center justify-between sticky top-0 bg-white '</span>&gt;
                        &lt;div className=<span class="hljs-string">'flex items-center justify-end gap-5'</span>&gt;
                            {<span class="hljs-comment">/*-- if user is signed out --*/</span>}
                            &lt;SignedOut&gt;
                                &lt;SignInButton mode=<span class="hljs-string">'modal'</span> /&gt;
                            &lt;/SignedOut&gt;
                            {<span class="hljs-comment">/*-- if user is signed in --*/</span>}
                            &lt;SignedIn&gt;
                                &lt;UserButton /&gt;
                            &lt;/SignedIn&gt;
                        &lt;/div&gt;
                    &lt;/nav&gt;

                    {children}
                &lt;/body&gt;
            &lt;/html&gt;
        &lt;/ClerkProvider&gt;
    );
}
</code></pre>
<p>After completing this, users will be prompted to create an account or sign in before they can access the application pages.</p>
<p>Finally, create a <a target="_blank" href="https://clerk.com">Clerk account</a> and set up a new Clerk application. Add your Clerk publishable and secret keys to the <code>.env.local</code> file in your project.</p>
<pre><code class="lang-bash">NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=&lt;publishable_key&gt;
CLERK_SECRET_KEY=&lt;secret_key&gt;
</code></pre>
<h2 id="heading-how-to-set-up-stream-in-a-nextjs-app">How to Set Up Stream in a Next.js app</h2>
<p>First, create a <a target="_blank" href="https://getstream.io/">Stream account</a> and set up an organization to house your app. Then, copy the following credentials into your <code>.env.local</code> file:</p>
<pre><code class="lang-bash">STREAM_APP_ID=&lt;your_app_id&gt;
NEXT_PUBLIC_STREAM_API_KEY=&lt;your_stream_api_key&gt;
STREAM_SECRET_KEY=&lt;your_stream_secret_key&gt;
NEXT_PUBLIC_FACETIME_HOST=http://localhost:3000/facetime
</code></pre>
<p>Next, install <a target="_blank" href="https://www.npmjs.com/package/@stream-io/video-react-sdk">Stream React Video SDK</a> and the <a target="_blank" href="https://getstream.io/video/docs/api/#installation">Stream Node.js SDK</a>.</p>
<pre><code class="lang-bash">npm install @stream-io/video-react-sdk @stream-io/node-sdk
</code></pre>
<p>Create a <code>providers</code> folder containing a <code>StreamVideoProvider.tsx</code> file 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> { tokenProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/actions/stream.actions"</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 } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useUser } <span class="hljs-keyword">from</span> <span class="hljs-string">"@clerk/nextjs"</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&gt;();

    <span class="hljs-keyword">const</span> { user, isLoaded } = useUser();

    useEffect(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-keyword">if</span> (!isLoaded || !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">const</span> client = <span class="hljs-keyword">new</span> StreamVideoClient({
            apiKey,
            user: {
                id: user?.id,
                name: user?.primaryEmailAddress?.emailAddress,
                image: user?.imageUrl,
            },
            tokenProvider, <span class="hljs-comment">//👉🏻 pending creation</span>
        });

        setVideoClient(client);
    }, [user, isLoaded]);

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

    <span class="hljs-keyword">return</span> &lt;StreamVideo client={videoClient}&gt;{children}&lt;/StreamVideo&gt;;
};
</code></pre>
<p>Let’s wrap the entire app with the <code>StreamVideoProvider</code> component, which initializes a Stream client to identify each user.</p>
<p>The <code>StreamVideoClient</code> function takes an object containing the API key, the user object with details from Clerk, and a <code>tokenProvider</code>.</p>
<p>Next, let’s create a <a target="_blank" href="https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations">Next.js server action</a> (<code>tokenProvider</code>) that generates the token.</p>
<p>Create an <code>actions</code> folder, add a <code>stream.actions.ts</code> file, and copy the following code snippet into the file:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">//👇🏻 tokenPrvoider function</span>
<span class="hljs-string">"use server"</span>;

<span class="hljs-keyword">import</span> { currentUser } <span class="hljs-keyword">from</span> <span class="hljs-string">"@clerk/nextjs/server"</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> currentUser();

    <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-comment">//👇🏻 generates a Stream user token</span>
    <span class="hljs-keyword">const</span> token = streamClient.generateUserToken({
        user_id: user.id,
        exp: expirationTime,
        validity_in_seconds: issuedAt,
    });
    <span class="hljs-comment">//👇🏻 returns the user token</span>
    <span class="hljs-keyword">return</span> token;
};
</code></pre>
<p>Finally, update the <code>RootLayout</code> function in the <code>app/layout.tsx</code> file by wrapping the entire application with the <code>StreamVideoProvider</code> component:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-string">"@stream-io/video-react-sdk/dist/css/styles.css"</span>;
<span class="hljs-keyword">import</span> { StreamVideoProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">"./providers/StreamVideoProvider"</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">RootLayout</span>(<span class="hljs-params">{
    children,
}: {
    children: React.ReactNode;
}</span>) </span>{
    <span class="hljs-keyword">return</span> (
        &lt;ClerkProvider&gt;
            &lt;html lang=<span class="hljs-string">'en'</span>&gt;
                &lt;body className={inter.className}&gt;
                    &lt;StreamVideoProvider&gt;
                        &lt;nav className=<span class="hljs-string">'w-full py-4 md:px-8 px-4 text-center flex items-center justify-between sticky top-0 bg-white '</span>&gt;
                            &lt;div className=<span class="hljs-string">'flex items-center justify-end gap-5'</span>&gt;
                                {<span class="hljs-comment">/*-- if user is signed out --*/</span>}
                                &lt;SignedOut&gt;
                                    &lt;SignInButton mode=<span class="hljs-string">'modal'</span> /&gt;
                                &lt;/SignedOut&gt;
                                {<span class="hljs-comment">/*-- if user is signed in --*/</span>}
                                &lt;SignedIn&gt;
                                    &lt;UserButton /&gt;
                                &lt;/SignedIn&gt;
                            &lt;/div&gt;
                        &lt;/nav&gt;

                        {children}
                    &lt;/StreamVideoProvider&gt;
                &lt;/body&gt;
            &lt;/html&gt;
        &lt;/ClerkProvider&gt;
    );
}
</code></pre>
<p>Congratulations! You've successfully integrated Stream into the Next.js app.</p>
<h2 id="heading-how-to-create-and-join-calls-with-stream">How to Create and Join Calls with Stream</h2>
<p>In this section, you'll learn how to create, schedule, and join calls using the Stream SDK. You'll also learn how to set up the meeting room with the necessary components and fetch upcoming calls from Stream.</p>
<h3 id="heading-creating-and-scheduling-calls">Creating and Scheduling calls</h3>
<p>To create an instant meeting, execute the <code>handleStartMeeting</code> function. It generates a random ID for the call and creates the meeting using the current date and the provided description.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useStreamVideoClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@stream-io/video-react-sdk"</span>;
<span class="hljs-keyword">import</span> { useUser } <span class="hljs-keyword">from</span> <span class="hljs-string">"@clerk/nextjs"</span>;
<span class="hljs-keyword">const</span> client = useStreamVideoClient();
<span class="hljs-keyword">const</span> { user } = useUser();

<span class="hljs-keyword">const</span> handleStartMeeting = <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-keyword">await</span> call.getOrCreate({
            data: {
                starts_at: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(<span class="hljs-built_in">Date</span>.now()).toISOString(),
                custom: {
                    description,
                },
            },
        });

        setFacetimeLink(<span class="hljs-string">`<span class="hljs-subst">${call.id}</span>`</span>);
        setShowMeetingLink(<span class="hljs-literal">true</span>);
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(error);
        alert(<span class="hljs-string">"Failed to create Meeting"</span>);
    }
};
</code></pre>
<p>The <code>call.getOrCreate()</code> function accepts an optional call description along with the current date and time to initiate the call.</p>
<p>It also allows you to schedule calls for a specific time in the future. In this case, you can specify the desired date and time, and Stream will automatically schedule the call for that period.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useStreamVideoClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@stream-io/video-react-sdk"</span>;
<span class="hljs-keyword">import</span> { useUser } <span class="hljs-keyword">from</span> <span class="hljs-string">"@clerk/nextjs"</span>;
<span class="hljs-keyword">const</span> client = useStreamVideoClient();
<span class="hljs-keyword">const</span> { user } = useUser();

<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-keyword">await</span> call.getOrCreate({
            data: {
                <span class="hljs-comment">//👇🏻 only necessary changes</span>
                starts_at: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(dateTime).toISOString(),
                custom: {
                    description,
                },
            },
        });
        setFacetimeLink(<span class="hljs-string">`<span class="hljs-subst">${call.id}</span>`</span>);
        setShowMeetingLink(<span class="hljs-literal">true</span>);
    } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(error);
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Failed to create Meeting"</span>);
    }
};
</code></pre>
<h3 id="heading-joining-calls-and-the-meeting-page">Joining calls and the Meeting Page</h3>
<p>Recall that the meeting link in the app is declared as:</p>
<pre><code class="lang-jsx"><span class="hljs-string">`<span class="hljs-subst">${process.env.NEXT_PUBLIC_FACETIME_HOST}</span>/<span class="hljs-subst">${facetimeLink}</span>`</span>
<span class="hljs-comment">// 👉🏻 format: &lt;http://localhost:3000/facetime/&gt;&lt;call.id&gt;</span>
</code></pre>
<p>Therefore, we need to create the <code>/facetime/&lt;callID&gt;</code> route to enable users to join a call. To do this, create a <code>facetime</code> folder with an <code>[id]</code> directory inside, and within that directory, add a <code>page.tsx</code> file. Then, 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> { useGetCallById } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/hooks/useGetCallById"</span>;
<span class="hljs-keyword">import</span> { useUser } <span class="hljs-keyword">from</span> <span class="hljs-string">"@clerk/nextjs"</span>;
<span class="hljs-keyword">import</span> {
    StreamCall,
    StreamTheme,
    PaginatedGridLayout,
    CallControls,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@stream-io/video-react-sdk"</span>;
<span class="hljs-keyword">import</span> { useParams, useRouter } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/navigation"</span>;
<span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">FaceTimePage</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> [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> router = useRouter();
    <span class="hljs-comment">//👇🏻 gets call details by ID</span>
    <span class="hljs-keyword">const</span> { call, isCallLoading } = useGetCallById(id);

    useEffect(<span class="hljs-function">() =&gt;</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();
        }
    }, [call, camMicEnabled]);

    <span class="hljs-comment">//👇🏻 enable users to join calls</span>
    <span class="hljs-keyword">const</span> handleJoin = <span class="hljs-function">() =&gt;</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 /&gt;
                    ) : (
                        &lt;div className=<span class="hljs-string">'flex flex-col items-center justify-center gap-5'</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-green-600 text-green-50'</span>
                                &gt;
                                    Join
                                &lt;/button&gt;
                                &lt;button
                                    onClick={<span class="hljs-function">() =&gt;</span> router.push(<span class="hljs-string">"/"</span>)}
                                    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>When users visit the meeting page, they are presented with a confirmation message, allowing them to confirm that they want to join the call.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726483083226/26ccb1d9-dc33-4a31-81a9-c4b0a3d00b91.png" alt="facetime-app-live" class="image--center mx-auto" width="3092" height="1834" loading="lazy"></p>
<p>In the code snippet above:</p>
<ul>
<li><p>The <code>useGetCallById</code> hook is a custom function that retrieves call details based on the call ID.</p>
</li>
<li><p>The <code>handleJoin</code> function allows users to join the call and then displays the <code>&lt;MeetingRoom /&gt;</code> component.</p>
</li>
</ul>
<p>Add the <code>MeetingRoom</code> component below the <code>FaceTimePage</code> component:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> MeetingRoom = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> router = useRouter();

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

    <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;PaginatedGridLayout /&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&gt;
        &lt;/section&gt;
    );
};
</code></pre>
<p>The <a target="_blank" href="https://getstream.io/video/docs/react/ui-components/core/call-layout/#paginatedgridlayout"><code>PaginatedGridLayout</code></a> arranges participants in a grid layout with pagination, allowing you to manage larger video calls by displaying a set number of participants per page.</p>
<p>The <code>CallControls</code> component provides built-in actions, such as muting, video toggling, and screen sharing, that can be performed during a call. Both components are part of the Stream SDK, making integration seamless.</p>
<p>Additionally, you can switch to the <a target="_blank" href="https://getstream.io/video/docs/react/ui-components/core/call-layout/#speakerlayout"><code>SpeakerLayout</code></a>, which highlights the dominant speaker or shared screen while displaying other participants in a smaller view.</p>
<p>Finally, create a <code>hooks</code> folder containing the <code>useGetCallById.ts</code> file and copy the code snippet below into the 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-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>The code snippet above filters the call list and <a target="_blank" href="https://getstream.io/video/docs/react/guides/querying-calls/#filters">returns the call with a matching ID</a>, allowing users to join the specified call.</p>
<h3 id="heading-retrieving-upcoming-calls">Retrieving Upcoming Calls</h3>
<p>To retrieve upcoming calls from Stream, you can create a custom hook that <a target="_blank" href="https://getstream.io/video/docs/react/guides/querying-calls/#calls-the-user-has-created-or-is-a-member-of">fetches all the calls created by the user</a>, as well as the calls they are a member of.</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> { useUser } <span class="hljs-keyword">from</span> <span class="hljs-string">"@clerk/nextjs"</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> useGetCalls = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> { user } = useUser();
    <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>);

    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 || !user?.id) <span class="hljs-keyword">return</span>;
            setIsLoading(<span class="hljs-literal">true</span>);
            <span class="hljs-keyword">try</span> {
                <span class="hljs-comment">//👇🏻 gets all the calls the user is featured in</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: user.id },
                            { members: { $in: [user.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, user?.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">//👇🏻 gets only calls that are yet to start</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-keyword">return</span> { upcomingCalls, isLoading };
};
</code></pre>
<p>The <code>useGetCalls</code> hook <a target="_blank" href="https://getstream.io/video/docs/react/guides/querying-calls/#calls-the-user-has-created-or-is-a-member-of">retrieves the list of upcoming calls</a>, which can then be displayed in the <code>UpcomingMeeting</code> modal.</p>
<p>Congratulations! You’ve completed the project for this tutorial.</p>
<p>Check out the live app <a target="_blank" href="https://facetime-on-stream.vercel.app/">here.</a></p>
<h2 id="heading-next-steps">Next Steps</h2>
<p>So far, you’ve learned how to build a video conferencing app. If you'd like to learn more about how you can leverage Stream to build scalable apps, then check out these resources:</p>
<ul>
<li><p><a target="_blank" href="https://getstream.io/chat/">How to integrate Stream Chat Messaging</a></p>
</li>
<li><p><a target="_blank" href="https://getstream.io/video/">How to integrate Stream Audio and Video calls</a></p>
</li>
<li><p><a target="_blank" href="https://getstream.io/activity-feeds/">How integrate Stream Activity Feeds</a></p>
</li>
</ul>
<h2 id="heading-before-we-end"><strong>Before We End...</strong></h2>
<p>I hope you found it insightful and that it has given you enough motivation on how to build apps using awesome developer tools.</p>
<p>These are some of my other most recent blog posts.</p>
<ul>
<li><p><a target="_blank" href="https://www.devtoolsacademy.com/blog/state-of-databases-2024">State of Databases for Serverless in 2024</a></p>
</li>
<li><p><a target="_blank" href="https://www.devtoolsacademy.com/blog/neon-vs-supabase"><strong>Neon Postgres vs Supabase</strong></a></p>
</li>
<li><p><a target="_blank" href="https://www.devtoolsacademy.com/blog/mongoDB-vs-postgreSQL"><strong>MongoDB vs. PostgreSQL</strong></a></p>
</li>
</ul>
<p>Check out <a target="_blank" href="https://theankurtyagi.com/">my blog</a> for more tutorials like this on awesome developer tools.</p>
<p>Follow me on <a target="_blank" href="https://x.com/theankurtyagi">Twitter</a> to stay updated on my side projects and ongoing learning.</p>
<p>Happy coding.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build an Invoice SaaS App with Next.js, Resend, Clerk and Neon Postgres ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, you will learn how to build an invoicing web app that allows users to add their bank information, manage a list of customers, and create and send invoices to customers. You'll also learn how to print and send React components as inv... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-an-invoice-saas-app-with-next-js-and-neon-postgres/</link>
                <guid isPermaLink="false">66c3754f9a8c3b7871c66a49</guid>
                
                    <category>
                        <![CDATA[ full stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ankur Tyagi ]]>
                </dc:creator>
                <pubDate>Thu, 01 Aug 2024 20:58:01 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/07/Orange---Yellow-Gradient-Make-Design-Blog-Banner--79-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, you will learn how to build an invoicing web app that allows users to add their bank information, manage a list of customers, and create and send invoices to customers. You'll also learn how to print and send React components as invoices and email templates directly from the application to the customer's email.</p>
<p>This will be a great project to help you learn how to put together full stack apps, and how to create an app where the backend can communicate with the frontend in real time.</p>
<p>While building the application, you will gain hands-on experience working with the following developer tools:</p>
<ul>
<li><a target="_blank" href="https://neon.tech/docs/introduction"><strong>Neon</strong></a>: a Postgres database that enables us to store and retrieve data easily within the application.</li>
<li><a target="_blank" href="https://clerk.com/"><strong>Clerk</strong></a>: a complete authentication system that ensures only authenticated users can perform specific actions within the application.</li>
<li><a target="_blank" href="https://www.npmjs.com/package/react-to-print"><strong>React-to-print</strong></a>: a package that allows us to convert and print React components as PDF files.</li>
<li><a target="_blank" href="https://resend.com/"><strong>Resend</strong></a> <strong>and</strong> <a target="_blank" href="https://react.email/docs/integrations/resend"><strong>React Email</strong></a>: for sending beautifully designed digital invoices directly to the customers' email.</li>
</ul>
<p><a target="_blank" href="https://github.com/tyaga001/invoice-saas-app-nextjs-neon-postgres">Here is the source code</a> (remember to give it a star ⭐).</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ol>
<li><a class="post-section-overview" href="#heading-what-is-neon">What is</a> Neon?</li>
<li><a class="post-section-overview" href="#heading-building-the-invoice-application-with-nextjs">Building the Invoice Application with Next.js</a></li>
<li><a class="post-section-overview" href="#heading-how-to-authenticate-users-using-clerk">How to Authenticate Users Using Clerk</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-neon-to-a-nextjs-app">How to Add Neon to a Next.js app</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-neon-serverless-driver-with-drizzle-orm-in-nextjs">How to Set Up Neon Serverless Driver with Drizzle ORM in Next.js</a></li>
<li><a class="post-section-overview" href="#heading-creating-the-api-endpoints-for-the-application">Creating the API endpoints for the application</a></li>
<li><a class="post-section-overview" href="#heading-how-to-print-and-download-invoices-in-nextjs">How to Print and Download Invoices in Next.js</a></li>
<li><a class="post-section-overview" href="#id=&quot;how-to-send-digital-invoices-with-resend-and-react-email&quot;">How to Send Digital Invoices with Resend and React Email</a></li>
<li><a class="post-section-overview" href="#heading-next-steps">Next Steps</a></li>
</ol>
<h2 id="heading-what-is-neon"><strong>What is Neon?</strong></h2>
<p><a target="_blank" href="https://github.com/neondatabase/neon">Neon</a> is an open-source, scalable, and efficient Postgres DB that separates compute from storage. This means that database computation processes (queries, transactions, and so on) are handled by one set of resources (compute), while the data itself is stored on a separate set of resources (storage).</p>
<p>This architecture allows for greater scalability and performance, making Neon a solid choice for modern web applications.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXcnJDCduaAEwKDQL2fc2lHsMj6g68thVN_txmoGMyD1ep-x1sWa5d-eiZ3AWjq4xkmGlF7JWxuEvrO9Os5qcEXbzBLep6tCpv-RSuCJjbLwe3hzP9870mfL6LcsH0HvV1x-ymzJ-PU1YjTFuQcihvwEUgeB?key=QrOqhkDtPIneanOaExEDaA" alt="Neon - a serverless Postgres database" width="1600" height="416" loading="lazy">
<em><a target="_blank" href="https://github.com/neondatabase/neon?tab=readme-ov-file">Neon - a serverless Postgres database</a></em></p>
<h2 id="heading-building-the-invoice-application-with-nextjs"><strong>Building the Invoice Application with Next.js</strong></h2>
<p>In this section, I'll guide you through building the various pages of the invoicing application using Next.js. The application is divided into six key pages, each serving a specific purpose:</p>
<ul>
<li><strong>Home Page</strong>: This is the landing page. It provides an overview of the application and signs users into the application.</li>
<li><strong>Settings Page</strong>: Here, users can update their bank information as it will be displayed on the invoices.</li>
<li><strong>Customers Page</strong>: This page allows users to manage their customer base, and add or delete customers when needed.</li>
<li><strong>Dashboard</strong>: The core of the application where users can create new invoices. Users can select a customer, enter the title and description of the invoice, and generate invoices.</li>
<li><strong>History Page</strong>: This page displays recently created invoices. It includes links that enable users to preview each invoice, providing a quick way to review past transactions.</li>
<li><strong>Print and Send Invoice Page</strong>: This page allows users to print and send invoices to customers.</li>
</ul>
<p>Before we proceed, create a TypeScript Next.js project by running the following code snippet in your terminal:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>npx create-next-app invoice-app-</span><span>with</span><span>-neon</span></p></td></tr></tbody></table>

<p>Add a <strong>types.d.ts</strong> file within the project folder. It will contain the type declarations for the variables within the application.</p>
<pre><code class="lang-javascript">interface Item {
    <span class="hljs-attr">id</span>: string;
    name: string;
    cost: number;
    quantity: number;
    price: number;
}

interface Invoice {
    id?: string,
    created_at?: string,
    <span class="hljs-attr">user_id</span>:  string,
    <span class="hljs-attr">customer_id</span>: number,
    <span class="hljs-attr">title</span>: string,
    <span class="hljs-attr">items</span>: string,
    <span class="hljs-attr">total_amount</span>: number,
}

interface Customer {
    <span class="hljs-attr">user_id</span>: string,
    <span class="hljs-attr">name</span>: string,
    <span class="hljs-attr">email</span>: string,
    <span class="hljs-attr">address</span>: string
}

interface BankInfo {
    <span class="hljs-attr">user_id</span>: string,
    <span class="hljs-attr">account_name</span>: string,
    <span class="hljs-attr">account_number</span>: number,
    <span class="hljs-attr">bank_name</span>: string,
    <span class="hljs-attr">currency</span>: string
}
</code></pre>
<h3 id="heading-home-page"><strong>Home Page</strong></h3>
<p>Copy the code snippet below into the <strong>app/page.tsx</strong> file. It displays brief information about the application and a button that redirects users to the dashboard or login page, depending on their authentication status.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">"next/link"</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">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-full'</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'p-8 h-[90vh] md:w-2/3 mx-auto text-center w-full flex flex-col items-center justify-center'</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 mb-4 md:text-4xl'</span>&gt;</span>
          Create invoices for your customers
        <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">className</span>=<span class="hljs-string">'opacity-70 mb-4 text-sm md:text-base leading-loose'</span>&gt;</span>
          Invoicer is an online invoicing software that helps you craft and
          print professional invoices for your customers for free! Keep your
          business and clients with one invoicing software.
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
          <span class="hljs-attr">href</span>=<span class="hljs-string">'/dashboard'</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">'rounded w-[200px] px-2 py-3 bg-blue-500 text-gray-50'</span>
        &gt;</span>
          LOG IN
        <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfxl_8niZbdRmGGgjCG66VCVO3dIZHO-oQ4TtSDjBRFqrU7qb6yGrVOBK4xqPYeFpYgddmDPA3hcw8X5bE1eqtdUP2Un9BHn_IM2CsjII17qap-VnDD8Qyo6ZW0TwFkTgWWNxXmxST6xcvr-KxIRYjK_2xg?key=QrOqhkDtPIneanOaExEDaA" alt="Invoice-app-home-page" width="1600" height="922" loading="lazy">
<em>Invoice-app-home-page</em></p>
<h3 id="heading-settings-page"><strong>Settings Page</strong></h3>
<p>Add a <strong>settings</strong> folder containing a <strong>page.tsx</strong> file within the Next.js app directory and copy the following code snippet into the file:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> { ChangeEvent, useEffect, useState, useCallback } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> SideNav <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/components/SideNav"</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">Settings</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">//👇🏻 default bank info</span>
    <span class="hljs-keyword">const</span> [bankInfo, setBankInfo] = useState({
        <span class="hljs-attr">account_name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">account_number</span>: <span class="hljs-number">1234567890</span>,
        <span class="hljs-attr">bank_name</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">currency</span>: <span class="hljs-string">""</span>,
 });

    <span class="hljs-comment">//👇🏻 bank info from the form entries</span>
    <span class="hljs-keyword">const</span> [inputBankInfo, setInputBankInfo] = useState({
        <span class="hljs-attr">accountName</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">accountNumber</span>: <span class="hljs-number">1234567890</span>,
        <span class="hljs-attr">bankName</span>: <span class="hljs-string">""</span>,
        <span class="hljs-attr">currency</span>: <span class="hljs-string">""</span>,
 });

    <span class="hljs-comment">//👇🏻 updates the form entries state</span>
    <span class="hljs-keyword">const</span> handleUpdateBankInfo = <span class="hljs-function">(<span class="hljs-params">
        e: ChangeEvent&lt;HTMLInputElement | HTMLSelectElement&gt;
 </span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> { name, value } = e.target;
        setInputBankInfo(<span class="hljs-function">(<span class="hljs-params">prevState</span>) =&gt;</span> ({
 ...prevState,
 [name]: value,
 }));
 };

    <span class="hljs-comment">//👇🏻 updates the bank info</span>
    <span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-function">(<span class="hljs-params">e: React.FormEvent&lt;HTMLFormElement&gt;</span>) =&gt;</span> {
        e.preventDefault();
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Tries to update bank info..."</span>);
 };
<span class="hljs-keyword">return</span> ()
}
</code></pre>
<p>The code snippet above shows that the page displays the user’s bank information and also allows the user to update it when necessary.</p>
<p>Return the UI elements below from the component:</p>
<pre><code class="lang-javascript"><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">Settings</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">//…React states and functions</span>

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-full'</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'min-h-[90vh] flex items-start'</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">SideNav</span> /&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'md:w-5/6 w-full h-full p-6'</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-2xl font-bold'</span>&gt;</span>Bank Information<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">className</span>=<span class="hljs-string">'opacity-70 mb-4'</span>&gt;</span>
            Update your bank account information
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex md:flex-row flex-col items-start justify-between w-full md:space-x-4'</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'md:w-1/3 w-full bg-blue-50 h-full p-3 rounded-md space-y-3'</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-sm opacity-75'</span>&gt;</span>
                Account Name: {bankInfo.account_name}
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-sm opacity-75'</span>&gt;</span>
                Account Number: {bankInfo.account_number}
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-sm opacity-75'</span>&gt;</span>
                Bank Name: {bankInfo.bank_name}
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-sm opacity-75'</span>&gt;</span>
                Currency: {bankInfo.currency}
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">form</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">'md:w-2/3 w-full p-3 flex flex-col'</span>
              <span class="hljs-attr">method</span>=<span class="hljs-string">'POST'</span>
              <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleSubmit}</span>
            &gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">'accountName'</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-sm'</span>&gt;</span>
                Account 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">type</span>=<span class="hljs-string">'text'</span>
                <span class="hljs-attr">name</span>=<span class="hljs-string">'accountName'</span>
                <span class="hljs-attr">id</span>=<span class="hljs-string">'accountName'</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">'border-[1px] p-2 rounded mb-3'</span>
                <span class="hljs-attr">required</span>
                <span class="hljs-attr">value</span>=<span class="hljs-string">{inputBankInfo.accountName}</span>
                <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleUpdateBankInfo}</span>
              /&gt;</span>

              <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">'accountNumber'</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-sm'</span>&gt;</span>
                Account Number
              <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">'number'</span>
                <span class="hljs-attr">name</span>=<span class="hljs-string">'accountNumber'</span>
                <span class="hljs-attr">id</span>=<span class="hljs-string">'accountNumber'</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">'border-[1px] p-2 rounded mb-3'</span>
                <span class="hljs-attr">required</span>
                <span class="hljs-attr">value</span>=<span class="hljs-string">{inputBankInfo.accountNumber}</span>
                <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleUpdateBankInfo}</span>
              /&gt;</span>

              <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">'bankName'</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-sm'</span>&gt;</span>
                Bank 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">type</span>=<span class="hljs-string">'text'</span>
                <span class="hljs-attr">name</span>=<span class="hljs-string">'bankName'</span>
                <span class="hljs-attr">id</span>=<span class="hljs-string">'bankName'</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">'border-[1px] p-2 rounded mb-3'</span>
                <span class="hljs-attr">required</span>
                <span class="hljs-attr">value</span>=<span class="hljs-string">{inputBankInfo.bankName}</span>
                <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleUpdateBankInfo}</span>
              /&gt;</span>

              <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">'currency'</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-sm'</span>&gt;</span>
                Currency
              <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">select</span>
                <span class="hljs-attr">name</span>=<span class="hljs-string">'currency'</span>
                <span class="hljs-attr">id</span>=<span class="hljs-string">'currency'</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">'border-[1px] p-2 rounded mb-3'</span>
                <span class="hljs-attr">required</span>
                <span class="hljs-attr">value</span>=<span class="hljs-string">{inputBankInfo.currency}</span>
                <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleUpdateBankInfo}</span>
              &gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">''</span>&gt;</span>Select<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">'$'</span>&gt;</span>USD<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">'€'</span>&gt;</span>EUR<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">'£'</span>&gt;</span>GBP<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex items-center 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">className</span>=<span class="hljs-string">'bg-blue-500 text-white p-2 w-[200px] rounded'</span>
                &gt;</span>
                  Update Bank Info
                <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">main</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfjj47wp06nbinvyDFg1Zl8udWgJWenfeu3wQ_b8_6KWP9bJAH69wCMsX5v0_XVm5-PF2K9mR_zyP7tJHLvmp2L2aLopuRQ8NiAVUVEa6WcSKV3gQOjGb2Va0227mk5OTCxQrro1uIQdwE7vyWI-rnqUkC6?key=QrOqhkDtPIneanOaExEDaA" alt="Invoice-app-settings-page" width="1600" height="836" loading="lazy">
<em>Invoice-app-settings-page</em></p>
<h3 id="heading-customers-page"><strong>Customers Page</strong></h3>
<p>Add a <strong>customers</strong> folder containing a <strong>page.tsx</strong> file within the Next.js directory and copy the code snippet below into the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> CustomersTable <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/CustomersTable"</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> SideNav <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/components/SideNav"</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">Customers</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [customerName, setCustomerName] = useState&lt;string&gt;(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [customerEmail, setCustomerEmail] = useState&lt;string&gt;(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [customerAddress, setCustomerAddress] = useState&lt;string&gt;(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [loading, setLoading] = useState&lt;boolean&gt;(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [customers, setCustomers] = useState([]);

  <span class="hljs-keyword">const</span> handleAddCustomer = <span class="hljs-function">(<span class="hljs-params">e: React.FormEvent&lt;HTMLFormElement&gt;</span>) =&gt;</span> {
    e.preventDefault();
    <span class="hljs-comment">// 👉🏻 createCustomer();</span>
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-full'</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'min-h-[90vh] flex items-start'</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">SideNav</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'md:w-5/6 w-full h-full p-6'</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-2xl font-bold'</span>&gt;</span>Customers<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">className</span>=<span class="hljs-string">'opacity-70 mb-4'</span>&gt;</span>Create and view all your customers<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

          <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-full'</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleAddCustomer}</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">className</span>=<span class="hljs-string">'w-full flex items-center space-x-4 mb-3'</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-1/2'</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Customer<span class="hljs-symbol">&amp;apos;</span>s 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">type</span>=<span class="hljs-string">'text'</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">'w-full p-2 border border-gray-200 rounded-sm'</span>
                  <span class="hljs-attr">value</span>=<span class="hljs-string">{customerName}</span>
                  <span class="hljs-attr">required</span>
                  <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setCustomerName(e.target.value)}
                /&gt;
              <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>

              <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-1/2'</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>Email Address<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">'email'</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">'w-full p-2 border border-gray-200 rounded-sm'</span>
                  <span class="hljs-attr">value</span>=<span class="hljs-string">{customerEmail}</span>
                  <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setCustomerEmail(e.target.value)}
                  required
                /&gt;
              <span class="hljs-tag">&lt;/<span class="hljs-name">section</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">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">'address'</span>&gt;</span>Billing Address<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">name</span>=<span class="hljs-string">'address'</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">'address'</span>
              <span class="hljs-attr">rows</span>=<span class="hljs-string">{3}</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">'w-full p-2 border border-gray-200 rounded-sm'</span>
              <span class="hljs-attr">value</span>=<span class="hljs-string">{customerAddress}</span>
              <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setCustomerAddress(e.target.value)}
              required
            /&gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">'bg-blue-500 text-white p-2 rounded-md mb-6'</span>
              <span class="hljs-attr">disabled</span>=<span class="hljs-string">{loading}</span>
            &gt;</span>
              {loading ? "Adding..." : "Add Customer"}
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

          <span class="hljs-tag">&lt;<span class="hljs-name">CustomersTable</span> <span class="hljs-attr">customers</span>=<span class="hljs-string">{customers}</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>The code snippet above allows users to view, create, and delete customers from the application.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXdd4_PjRZlb7a65BP3PJIItYL0b7GuwMqwiMstNFqPsOl7n5lNIehqAZFK33YPMSHBtbPeRg-LwRmMwv0ASz1PBfC9Bo8YWaNGJcO_heST76rrsB7R6c0PDeXeC5B9AH2TfWriGj4SNC7FGO1BcEm8cEwol?key=QrOqhkDtPIneanOaExEDaA" alt="Invoice-app-customer-page" width="1600" height="917" loading="lazy">
<em>Invoice-app-customer-page</em></p>
<h3 id="heading-dashboard-page"><strong>Dashboard Page</strong></h3>
<p>Create a dashboard folder containing a page.tsx within the Next.js app directory and copy the code snippet below into the file:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> InvoiceTable <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/components/InvoiceTable"</span>;
<span class="hljs-keyword">import</span> React, { useState, useEffect, 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> SideNav <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/components/SideNav"</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">Dashboard</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { isLoaded, isSignedIn, user } = useUser();
  <span class="hljs-keyword">const</span> [itemList, setItemList] = useState&lt;Item[]&gt;([]);
  <span class="hljs-keyword">const</span> [customer, setCustomer] = useState&lt;string&gt;(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [invoiceTitle, setInvoiceTitle] = useState&lt;string&gt;(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [itemCost, setItemCost] = useState&lt;number&gt;(<span class="hljs-number">1</span>);
  <span class="hljs-keyword">const</span> [itemQuantity, setItemQuantity] = useState&lt;number&gt;(<span class="hljs-number">1</span>);
  <span class="hljs-keyword">const</span> [itemName, setItemName] = useState&lt;string&gt;(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [customers, setCustomers] = useState([]);
  <span class="hljs-keyword">const</span> router = useRouter();

  <span class="hljs-keyword">const</span> handleAddItem = <span class="hljs-function">(<span class="hljs-params">e: React.FormEvent</span>) =&gt;</span> {
    e.preventDefault();
    <span class="hljs-keyword">if</span> (itemName.trim() &amp;&amp; itemCost &gt; <span class="hljs-number">0</span> &amp;&amp; itemQuantity &gt;= <span class="hljs-number">1</span>) {
      setItemList([
        ...itemList,
        {
          <span class="hljs-attr">id</span>: <span class="hljs-built_in">Math</span>.random().toString(<span class="hljs-number">36</span>).substring(<span class="hljs-number">2</span>, <span class="hljs-number">9</span>),
          <span class="hljs-attr">name</span>: itemName,
          <span class="hljs-attr">cost</span>: itemCost,
          <span class="hljs-attr">quantity</span>: itemQuantity,
          <span class="hljs-attr">price</span>: itemCost * itemQuantity,
        },
      ]);
    }

    setItemName(<span class="hljs-string">""</span>);
    setItemCost(<span class="hljs-number">0</span>);
    setItemQuantity(<span class="hljs-number">0</span>);
  };

  <span class="hljs-keyword">const</span> getTotalAmount = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">let</span> total = <span class="hljs-number">0</span>;
    itemList.forEach(<span class="hljs-function">(<span class="hljs-params">item</span>) =&gt;</span> {
      total += item.price;
    });
    <span class="hljs-keyword">return</span> total;
  };

  <span class="hljs-keyword">const</span> handleFormSubmit = <span class="hljs-function">(<span class="hljs-params">e: React.FormEvent&lt;HTMLFormElement&gt;</span>) =&gt;</span> {
    e.preventDefault();
    <span class="hljs-comment">//👉🏻 createInvoice();</span>
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-full'</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'min-h-[90vh] flex items-start'</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">SideNav</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'md:w-5/6 w-full h-full p-6'</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'font-bold text-2xl mb-3'</span>&gt;</span>Add new invoice<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">className</span>=<span class="hljs-string">'w-full flex flex-col'</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleFormSubmit}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">'customer'</span>&gt;</span>Customer<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">select</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">'border-[1px] p-2 rounded-sm mb-3'</span>
              <span class="hljs-attr">required</span>
              <span class="hljs-attr">value</span>=<span class="hljs-string">{customer}</span>
              <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setCustomer(e.target.value)}
            &gt;
              {customers.map((customer: any) =&gt; (
                <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{customer.id}</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{customer.name}</span>&gt;</span>
                  {customer.name}
                <span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
              ))}
            <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">'title'</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">className</span>=<span class="hljs-string">'border-[1px] rounded-sm mb-3 py-2 px-3'</span>
              <span class="hljs-attr">required</span>
              <span class="hljs-attr">value</span>=<span class="hljs-string">{invoiceTitle}</span>
              <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setInvoiceTitle(e.target.value)}
            /&gt;

            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-full flex justify-between flex-col'</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'my-4 font-bold'</span>&gt;</span>Items List<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>

              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex space-x-3'</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex flex-col w-1/4'</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">'itemName'</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-sm'</span>&gt;</span>
                    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">type</span>=<span class="hljs-string">'text'</span>
                    <span class="hljs-attr">name</span>=<span class="hljs-string">'itemName'</span>
                    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">'Name'</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">'py-2 px-4 mb-6 bg-gray-100'</span>
                    <span class="hljs-attr">value</span>=<span class="hljs-string">{itemName}</span>
                    <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setItemName(e.target.value)}
                  /&gt;
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex flex-col w-1/4'</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">'itemCost'</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-sm'</span>&gt;</span>
                    Cost
                  <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                    <span class="hljs-attr">type</span>=<span class="hljs-string">'number'</span>
                    <span class="hljs-attr">name</span>=<span class="hljs-string">'itemCost'</span>
                    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">'Cost'</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">'py-2 px-4 mb-6 bg-gray-100'</span>
                    <span class="hljs-attr">value</span>=<span class="hljs-string">{itemCost}</span>
                    <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setItemCost(Number(e.target.value))}
                  /&gt;
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex flex-col justify-center w-1/4'</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">'itemQuantity'</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-sm'</span>&gt;</span>
                    Quantity
                  <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                    <span class="hljs-attr">type</span>=<span class="hljs-string">'number'</span>
                    <span class="hljs-attr">name</span>=<span class="hljs-string">'itemQuantity'</span>
                    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">'Quantity'</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">'py-2 px-4 mb-6 bg-gray-100'</span>
                    <span class="hljs-attr">value</span>=<span class="hljs-string">{itemQuantity}</span>
                    <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setItemQuantity(Number(e.target.value))}
                  /&gt;
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex flex-col justify-center w-1/4'</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-sm'</span>&gt;</span>Price<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'py-2 px-4 mb-6 bg-gray-100'</span>&gt;</span>
                    {Number(itemCost * itemQuantity).toLocaleString("en-US")}
                  <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">button</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">'bg-blue-500 text-gray-100 w-[100px] p-2 rounded'</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleAddItem}</span>
              &gt;</span>
                Add Item
              <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">InvoiceTable</span> <span class="hljs-attr">itemList</span>=<span class="hljs-string">{itemList}</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">'bg-blue-800 text-gray-100 w-full p-4 rounded my-6'</span>
              <span class="hljs-attr">type</span>=<span class="hljs-string">'submit'</span>
            &gt;</span>
              SAVE &amp; PREVIEW INVOICE
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>The code snippet above displays a form that accepts the invoice details, such as the customer’s name, invoice title, and items list needed to create an invoice.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXcUHH9gL0R6IRq-WSuKxwiTyNLM0Hae4uqYjIPXBswcEDG_zNfk7-QBLGj1Ht-RC5zbPkp6JddjSgIEwvkNeID6756C7i_uA-_vq8kgTDU-tuA6FqORWxtaJ8Jc53XdOULfmGOmEHSsiGRbTuXuth957Hkt?key=QrOqhkDtPIneanOaExEDaA" alt="Invoice-app-dashboard" width="1600" height="866" loading="lazy">
<em>Invoice-app-dashboard</em></p>
<h3 id="heading-history-page"><strong>History Page</strong></h3>
<p>Create a <strong>history</strong> folder containing a <strong>page.tsx</strong> file within the Next.js app directory and copy the following code into the file:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> { useState, useEffect, useCallback } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">"next/link"</span>;
<span class="hljs-keyword">import</span> SideNav <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/components/SideNav"</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">History</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { isLoaded, isSignedIn, user } = useUser();
  <span class="hljs-keyword">const</span> [invoices, setInvoices] = useState&lt;Invoice[]&gt;([]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-full'</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'min-h-[90vh] flex items-start'</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">SideNav</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'md:w-5/6 w-full h-full p-6'</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-2xl font-bold'</span>&gt;</span>History<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">className</span>=<span class="hljs-string">'opacity-70 mb-4'</span>&gt;</span>View all your invoices and their status<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>

          {invoices.map((invoice) =&gt; (
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">'bg-blue-50 w-full mb-3 rounded-md p-3 flex items-center justify-between'</span>
              <span class="hljs-attr">key</span>=<span class="hljs-string">{invoice.id}</span>
            &gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-sm text-gray-500 mb-2'</span>&gt;</span>
                  Invoice - #0{invoice.id} issued to{" "}
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'font-bold'</span>&gt;</span>{invoice.customer_id}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-lg font-bold mb-[1px]'</span>&gt;</span>
                  {Number(invoice.total_amount).toLocaleString()}
                <span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">href</span>=<span class="hljs-string">{{</span>
                  <span class="hljs-attr">pathname:</span> `/<span class="hljs-attr">invoices</span>/${<span class="hljs-attr">invoice.id</span>}`,
                  <span class="hljs-attr">query:</span> { <span class="hljs-attr">customer:</span> <span class="hljs-attr">invoice.customer_id</span> },
                }}
                <span class="hljs-attr">className</span>=<span class="hljs-string">'bg-blue-500 text-blue-50 rounded p-3'</span>
              &gt;</span>
                Preview
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          ))}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>The code snippet above displays the recently created invoices and enables users to preview them when needed.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXfn94sQF287nrzVJb3FB3rO3zkfidF87Amtx4xliIM93iK_I30dAEZEZaDrt7YMX2e_Zi2o0lMJYHvqudFrlQA880nL8NbO0Rsii_n_sdMVV1Lp6DHbOT7eo-RLhAM7VUfxekxVyXjlpzqSn5LaX28_vZOz?key=QrOqhkDtPIneanOaExEDaA" alt="Invoice-app-history-page" width="1600" height="923" loading="lazy">
<em>Invoice-app-history-page</em></p>
<h2 id="heading-how-to-authenticate-users-using-clerk"><strong>How to Authenticate Users Using Clerk</strong></h2>
<p><a target="_blank" href="https://github.com/clerkinc">Clerk</a> is a complete user management platform that enables you to add various forms of authentication to your software applications. It provides easy-to-use, flexible UI components and APIs that can be integrated seamlessly into your application.</p>
<p>Install the <a target="_blank" href="https://clerk.com/docs/quickstarts/nextjs">Clerk Next.js SDK</a> by running the following code snippet in your terminal:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>npm install @clerk/nextjs</span></p></td></tr></tbody></table>

<p>Create a <code>middleware.ts</code> file within the Next.js src folder and copy the code snippet below into the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { clerkMiddleware, createRouteMatcher } <span class="hljs-keyword">from</span> <span class="hljs-string">"@clerk/nextjs/server"</span>;

<span class="hljs-comment">// the createRouteMatcher function accepts an array of routes to be protected</span>
<span class="hljs-keyword">const</span> protectedRoutes = createRouteMatcher([
    <span class="hljs-string">"/customers"</span>,
    <span class="hljs-string">"/settings"</span>,
    <span class="hljs-string">"/dashboard"</span>,
    <span class="hljs-string">"/history"</span>,
    <span class="hljs-string">"/invoices(.*)"</span>,
]);

<span class="hljs-comment">// protects the route</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> clerkMiddleware(<span class="hljs-function">(<span class="hljs-params">auth, req</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (protectedRoutes(req)) {
        auth().protect();
 }
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> config = {
    <span class="hljs-attr">matcher</span>: [<span class="hljs-string">"/((?!.*\\..*|_next).*)"</span>, <span class="hljs-string">"/"</span>, <span class="hljs-string">"/(api|trpc)(.*)"</span>],
};
</code></pre>
<p>The <strong><code>createRouteMatcher()</code></strong> function accepts an array containing routes to be protected from unauthenticated users, and the <strong><code>clerkMiddleware()</code></strong> function ensures the routes are protected.</p>
<p>Next, import the following Clerk components into the <strong>app/layout.tsx</strong> file and update the <strong><code>RootLayout</code></strong> function as shown below:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {
    ClerkProvider,
    SignInButton,
    SignedIn,
    SignedOut,
    UserButton,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@clerk/nextjs"</span>;
<span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">"next/link"</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">RootLayout</span>(<span class="hljs-params">{
    children,
}: Readonly&lt;{
    children: React.ReactNode;
}&gt;</span>) </span>{
    <span class="hljs-keyword">return</span> (
 <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ClerkProvider</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">body</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{inter.className}</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex justify-between items-center h-[10vh] px-8 border-b-[1px]'</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">'/'</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-xl font-extrabold text-blue-700'</span>&gt;</span>
 Invoicer
 <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex items-center gap-5'</span>&gt;</span>
                            {/*-- if user is signed out --*/}
 <span class="hljs-tag">&lt;<span class="hljs-name">SignedOut</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">SignInButton</span> <span class="hljs-attr">mode</span>=<span class="hljs-string">'modal'</span> /&gt;</span>
 <span class="hljs-tag">&lt;/<span class="hljs-name">SignedOut</span>&gt;</span>
                            {/*-- if user is signed in --*/}
 <span class="hljs-tag">&lt;<span class="hljs-name">SignedIn</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">'/dashboard'</span> <span class="hljs-attr">className</span>=<span class="hljs-string">''</span>&gt;</span>
 Dashboard
 <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">UserButton</span> <span class="hljs-attr">showName</span> /&gt;</span>
 <span class="hljs-tag">&lt;/<span class="hljs-name">SignedIn</span>&gt;</span>
 <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
 <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>

                    {children}
 <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>
 <span class="hljs-tag">&lt;/<span class="hljs-name">ClerkProvider</span>&gt;</span></span>
 );
}
</code></pre>
<p>When a user is not signed in, the <a target="_blank" href="https://clerk.com/docs/components/unstyled/sign-in-button">Sign in button</a> component is rendered.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXe7-OxFwVNEjJ_vvM9zo7j-d1jVKcYj1EXoV-Kk5_WR3k3Ie3h1wXnr2VB_Df5rbc4OJ_uK3wtJ4g1iTfYNrsOqTDu4oMrljRNxhh0xQCVMkSyO_zrrUxmBaT-iBgAkiAKk4Tkoj17stTyY-Y3VP72BbjFL?key=QrOqhkDtPIneanOaExEDaA" alt="Clerk-Auth-Signup-Page" width="1600" height="903" loading="lazy">
<em>Clerk-Auth-Signup-Page</em></p>
<p>Then, after signing into the application, Clerk's <a target="_blank" href="https://clerk.com/docs/components/user/user-button">User Button component</a> and a link to the dashboard are displayed.</p>
<p>Next, create a <a target="_blank" href="https://clerk.com/">Clerk account</a> and add a new application project.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXcu_CxSCF4Gy9AxT0QGVt8Ia1xcU3XrqLsOMxi9v1mqs7qMIHXQGPVHabyfIUkJ9YfyzkXcy-7Q85fSUz9_r1FPxY_9R8RtFuMxiR0CeNZjLqlgkNLXLG43L_EIdeyK1Dwl5tJd7PvBrG7LeHb-NJ8-I0o?key=QrOqhkDtPIneanOaExEDaA" alt="Clerk-Auth-Project-Page" width="1600" height="939" loading="lazy">
<em>Clerk-Auth-Project-Page</em></p>
<p>Select <strong>email</strong> as the authentication method and create the Clerk project.</p>
<p>Finally, add your Clerk publishable and secret keys into the .<strong>env.local</strong> file.</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=&lt;your_publishable_key&gt;</span><span><br></span><span>CLERK_SECRET_KEY=&lt;your_secret_key&gt;</span></p></td></tr></tbody></table>

<p>Clerk provides various ways to <a target="_blank" href="https://clerk.com/docs/references/nextjs/read-session-data">read user's data</a> on the client and the server, which is essential for identifying users within the application.</p>
<h2 id="heading-how-to-add-neon-to-a-nextjs-app"><strong>How to Add Neon to a Next.js app</strong></h2>
<p><a target="_blank" href="https://github.com/tyaga001/awesome-neon">Neon</a> supports multiple frameworks and libraries and provides clear and detailed documentation on adding Neon to them. The Neon serverless driver lets you connect to and interact with Neon in a Next.js application.</p>
<p>Before we proceed, let’s <a target="_blank" href="https://neon.tech/docs/guides/nextjs">create a Neon account and project</a>.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXdbDT3O2Kdn_GAbeMGyegKJB6dDkFnXRC9YyW_YTkGTyZuC3GYpb9ohemo3iatRjq7Cpx0jnwCnY5MXy0xkK6Nu7hf18rvZZOIsRXJi3zZUsTTAaOwDpN61WtnFVpIclISdBDZquVFtEFG8ZB9tg6bVg2wD?key=QrOqhkDtPIneanOaExEDaA" alt="Neon-postgres-all-project-dashboard" width="1600" height="934" loading="lazy">
<em>Neon-postgres-all-project-dashboard</em></p>
<p>Within your project dashboard, you'll find a database connection string. You'll use this to interact with your Neon database.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXf62euRKYINnsRnREwseLaCeBpGc9kKGTk1sIC4xO36QGpwCaYUhLva-71rrhJ_Z7sb9v1dN0Tz-3DtCCrKPy62duD2afc5MDVMpLi9wgvtw-rKg3o4huDZIbbxxSiwuftKwmtq6iVNAeQwkx1OohSKAA4b?key=QrOqhkDtPIneanOaExEDaA" alt="Neon-project-dashboard" width="1600" height="949" loading="lazy">
<em>Neon-project-dashboard</em></p>
<p>Next, install the Neon Serverless package into the Next.js project:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>npm install </span><span>@neondatabase</span><span>/serverless</span></p></td></tr></tbody></table>

<p>Copy your database connection string into the <strong>.env.local</strong> file.</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>NEON_DATABASE_URL=</span><span>"postgres://&lt;user&gt;:&lt;password&gt;@&lt;endpoint_hostname&gt;.neon.tech:&lt;port&gt;/&lt;dbname&gt;?sslmode=require"</span></p></td></tr></tbody></table>

<p>Create a <strong>db</strong> folder containing an <strong>index.ts</strong> file within the Next.js app directory and copy the code snippet below into the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { neon } <span class="hljs-keyword">from</span> <span class="hljs-string">'@neondatabase/serverless'</span>;

<span class="hljs-keyword">if</span> (!process.env.NEON_DATABASE_URL) {
  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'NEON_DATABASE_URL must be a Neon postgres connection string'</span>)
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getDBVersion = <span class="hljs-keyword">async</span>() =&gt; {
    <span class="hljs-keyword">const</span> sql = neon(process.env.NEON_DATABASE_URL!);
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> sql<span class="hljs-string">`SELECT version()`</span>;
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">version</span>: response[<span class="hljs-number">0</span>].version }
}
</code></pre>
<p>Convert the <strong>app/page.tsx</strong> file to a server component and execute the <strong><code>getDBVersion()</code></strong> function:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { getDBVersion } <span class="hljs-keyword">from</span> <span class="hljs-string">"./db"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> { version } = <span class="hljs-keyword">await</span> getDBVersion();
    <span class="hljs-built_in">console</span>.log({version})

   <span class="hljs-keyword">return</span> (<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{/** -- UI elements -- */}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>)

}
</code></pre>
<p>The <strong><code>getDBVersion()</code></strong> function establishes a connection with the Neon database and allows us to run SQL queries using the Postgres client. This function returns the database version, which is then logged to the console.</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>{</span><span><br></span><span>version: </span><span>'PostgreSQL 16.3 on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit'</span><span><br></span><span>}</span></p></td></tr></tbody></table>

<p>Congratulations – you’ve successfully added Neon to your Next.js app.</p>
<p>However, interacting with the Neon database by writing SQL queries directly can require extra learning or introduce complexities for developers who are not familiar with SQL. It can also lead to errors or performance issues when performing complex queries.</p>
<p>This is why Neon supports database ORMs such as Drizzle ORM, which provide a higher-level interface for interacting with the database. <a target="_blank" href="https://orm.drizzle.team/docs/overview">Drizzle ORM</a> enables you to write complex query functions and interact with the database easily using TypeScript.</p>
<h2 id="heading-how-to-set-up-neon-serverless-driver-with-drizzle-orm-in-nextjs"><strong>How to Set Up Neon Serverless Driver with Drizzle ORM in Next.js</strong></h2>
<p>Drizzle ORM lets you query data and perform various operations on the database using simple TypeScript query commands. It is lightweight, typesafe, and easy to use.</p>
<p>First, you'll need to install the <a target="_blank" href="https://orm.drizzle.team/kit-docs/overview">Drizzle Kit</a> and the <a target="_blank" href="https://orm.drizzle.team/docs/overview">Drizzle ORM</a> package.</p>
<p>Drizzle Kit lets you manage the database schema and migrations.</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>npm i drizzle-orm</span><span><br></span><span>npm i -D drizzle-kit</span></p></td></tr></tbody></table>

<p>Inside the <strong>db</strong> folder, add an <strong>actions.ts</strong>, and <strong>schema.ts</strong> file:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>cd db</span><span><br></span><span>touch actions.ts schema.ts</span></p></td></tr></tbody></table>

<p>The actions.ts file will contain the required database queries and operations, while the schema.ts file will define the database schema for the invoicing application.</p>
<h3 id="heading-database-design-for-the-invoice-application"><strong>Database Design for the invoice application</strong></h3>
<p>Recall that users can add customers, update their bank information, and create invoices within the application. So you need to create database tables for the data in Neon.</p>
<p>The user's ID will be used as a foreign key to identify each row of data that belongs to a specific user.</p>
<p>Copy the code snippet below into the <strong>db/schema.ts</strong> file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {  text, serial, pgTable, timestamp, numeric } <span class="hljs-keyword">from</span> <span class="hljs-string">"drizzle-orm/pg-core"</span>;

<span class="hljs-comment">//👇🏻 invoice table with its column types</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> invoicesTable = pgTable(<span class="hljs-string">"invoices"</span>, {
    <span class="hljs-attr">id</span>: serial(<span class="hljs-string">"id"</span>).primaryKey().notNull(),
    <span class="hljs-attr">owner_id</span>: text(<span class="hljs-string">"owner_id"</span>).notNull(),
    <span class="hljs-attr">customer_id</span>: text(<span class="hljs-string">"customer_id"</span>).notNull(),
    <span class="hljs-attr">title</span>: text(<span class="hljs-string">"title"</span>).notNull(),
    <span class="hljs-attr">items</span>: text(<span class="hljs-string">"items"</span>).notNull(),
    <span class="hljs-attr">created_at</span>: timestamp(<span class="hljs-string">"created_at"</span>).defaultNow(),
    <span class="hljs-attr">total_amount</span>: numeric(<span class="hljs-string">"total_amount"</span>).notNull(),
});

<span class="hljs-comment">//👇🏻 customers table with its column types</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> customersTable = pgTable(<span class="hljs-string">"customers"</span>, {
    <span class="hljs-attr">id</span>: serial(<span class="hljs-string">"id"</span>).primaryKey().notNull(),
    <span class="hljs-attr">created_at</span>: timestamp(<span class="hljs-string">"created_at"</span>).defaultNow(),
    <span class="hljs-attr">owner_id</span>: text(<span class="hljs-string">"owner_id"</span>).notNull(),
    <span class="hljs-attr">name</span>: text(<span class="hljs-string">"name"</span>).notNull(),
    <span class="hljs-attr">email</span>: text(<span class="hljs-string">"email"</span>).notNull(),
    <span class="hljs-attr">address</span>: text(<span class="hljs-string">"address"</span>).notNull(),
})

<span class="hljs-comment">//👇🏻 bank_info table with its column types</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> bankInfoTable = pgTable(<span class="hljs-string">"bank_info"</span>, {
    <span class="hljs-attr">id</span>: serial(<span class="hljs-string">"id"</span>).primaryKey().notNull(),
    <span class="hljs-attr">owner_id</span>: text(<span class="hljs-string">"owner_id"</span>).notNull().unique(),
    <span class="hljs-attr">bank_name</span>: text(<span class="hljs-string">"bank_name"</span>).notNull(),
    <span class="hljs-attr">account_number</span>: numeric(<span class="hljs-string">"account_number"</span>).notNull(),
    <span class="hljs-attr">account_name</span>: text(<span class="hljs-string">"account_name"</span>).notNull(),
    <span class="hljs-attr">created_at</span>: timestamp(<span class="hljs-string">"created_at"</span>).defaultNow(),
    <span class="hljs-attr">currency</span>: text(<span class="hljs-string">"currency"</span>).notNull(),
})
</code></pre>
<p>The actions.ts file will contain the various database operations required within the application. First, add the code snippet below to the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { invoicesDB, customersDB, bankInfoDB } <span class="hljs-keyword">from</span> <span class="hljs-string">"."</span>;
<span class="hljs-keyword">import</span> { invoicesTable, customersTable, bankInfoTable } <span class="hljs-keyword">from</span> <span class="hljs-string">'./schema'</span>;
<span class="hljs-keyword">import</span> { desc, eq } <span class="hljs-keyword">from</span> <span class="hljs-string">"drizzle-orm"</span>;

<span class="hljs-comment">//👇🏻 add a new row to the invoices table</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> createInvoice = <span class="hljs-keyword">async</span> (invoice: any) =&gt; {
    <span class="hljs-keyword">await</span> invoicesDB.insert(invoicesTable).values({
    <span class="hljs-attr">owner_id</span>: invoice.user_id,
    <span class="hljs-attr">customer_id</span>: invoice.customer_id,
    <span class="hljs-attr">title</span>: invoice.title,
    <span class="hljs-attr">items</span>: invoice.items,
    <span class="hljs-attr">total_amount</span>: invoice.total_amount,
 });
};

<span class="hljs-comment">//👇🏻 get all user's invoices</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getUserInvoices = <span class="hljs-keyword">async</span> (user_id: string) =&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> invoicesDB.select().from(invoicesTable).where(eq(invoicesTable.owner_id, user_id)).orderBy(desc(invoicesTable.created_at));
};

<span class="hljs-comment">//👇🏻 get single invoice</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getSingleInvoice = <span class="hljs-keyword">async</span> (id: number) =&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> invoicesDB.select().from(invoicesTable).where(eq(invoicesTable.id, id));
};
</code></pre>
<p>The <strong><code>createInvoice</code></strong> function accepts invoice details as a parameter and adds a new row of data to its invoice table. The <strong><code>getUserInvoices</code></strong> function filters the table and returns an array of invoices created by the user. The <strong><code>getSingleInvoice</code></strong> function accepts an invoice ID, filters the table, and returns the invoice with a matching ID.</p>
<p>Add the following functions to the db/actions file:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//👇🏻 get customers list</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getCustomers = <span class="hljs-keyword">async</span> (user_id: string) =&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> customersDB.select().from(customersTable).where(eq(customersTable.owner_id, user_id)).orderBy(desc(customersTable.created_at));
};

<span class="hljs-comment">//👇🏻 get single customer</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getSingleCustomer = <span class="hljs-keyword">async</span> (name: string) =&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> customersDB.select().from(customersTable).where(eq(customersTable.name, name));
};

<span class="hljs-comment">//👇🏻 add a new row to the customers table</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> addCustomer = <span class="hljs-keyword">async</span> (customer: Customer) =&gt; {
    <span class="hljs-keyword">await</span> customersDB.insert(customersTable).values({
        <span class="hljs-attr">owner_id</span>: customer.user_id,
        <span class="hljs-attr">name</span>: customer.name,
        <span class="hljs-attr">email</span>: customer.email,
        <span class="hljs-attr">address</span>: customer.address,
 });
};

<span class="hljs-comment">//👇🏻 delete a customer</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> deleteCustomer = <span class="hljs-keyword">async</span> (id: number) =&gt; {
  <span class="hljs-keyword">await</span> customersDB.delete(customersTable).where(eq(customersTable.id, id));
};
</code></pre>
<p>This code snippet enables users to retrieve all their customers from the database, get a single customer via its ID, add new customers, and delete customers from the <strong>customers</strong> table.</p>
<p>Finally, add this also to the <strong>db/actions.ts</strong> file:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//👇🏻 get user's bank info</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getUserBankInfo = <span class="hljs-keyword">async</span> (user_id: string) =&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> bankInfoDB.select().from(bankInfoTable).where(eq(bankInfoTable.owner_id, user_id));
};

<span class="hljs-comment">//👇🏻 update bank info table</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> updateBankInfo = <span class="hljs-keyword">async</span> (info: any) =&gt; {
<span class="hljs-keyword">await</span> bankInfoDB.insert(bankInfoTable)
 .values({
        <span class="hljs-attr">owner_id</span>: info.user_id,
        <span class="hljs-attr">bank_name</span>: info.bank_name,
        <span class="hljs-attr">account_number</span>: info.account_number,
        <span class="hljs-attr">account_name</span>: info.account_name,
        <span class="hljs-attr">currency</span>: info.currency,
 })
 .onConflictDoUpdate({
            <span class="hljs-attr">target</span>: bankInfoTable.owner_id,
            <span class="hljs-attr">set</span>: {
                <span class="hljs-attr">bank_name</span>: info.bank_name,
                <span class="hljs-attr">account_number</span>: info.account_number,
                <span class="hljs-attr">account_name</span>: info.account_name,
                <span class="hljs-attr">currency</span>: info.currency,
 },
 });
};
</code></pre>
<p>The <strong><code>getUserBankInfo</code></strong> function fetches the user’s bank information from the database, while the <strong><code>updateBankInfo</code></strong> function updates it. If the user already has one, the function updates it with the new details – otherwise, it creates a new entry.</p>
<p>Next, update the <strong>db/index.ts</strong> file to connect to the Neon database and export the Drizzle instance for each table. This will be used to execute typesafe SQL queries against your Postgres database hosted on Neon.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { neon } <span class="hljs-keyword">from</span> <span class="hljs-string">'@neondatabase/serverless'</span>;
<span class="hljs-keyword">import</span> { drizzle } <span class="hljs-keyword">from</span> <span class="hljs-string">'drizzle-orm/neon-http'</span>;
<span class="hljs-keyword">import</span> { invoicesTable, customersTable, bankInfoTable } <span class="hljs-keyword">from</span> <span class="hljs-string">'./schema'</span>;

<span class="hljs-keyword">if</span> (!process.env.NEON_DATABASE_URL) {
  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'DATABASE_URL must be a Neon postgres connection string'</span>)
}
<span class="hljs-keyword">const</span> sql = neon(process.env.NEON_DATABASE_URL!);


<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> invoicesDB = drizzle(sql, {
  <span class="hljs-attr">schema</span>: { invoicesTable }
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> customersDB = drizzle(sql, {
  <span class="hljs-attr">schema</span>: { customersTable }
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> bankInfoDB = drizzle(sql, {
  <span class="hljs-attr">schema</span>: { bankInfoTable }
});
</code></pre>
<p>Create a <strong>drizzle.config.ts</strong> file at the root of the Next.js folder and add the following configuration. Ensure you install the <a target="_blank" href="https://www.npmjs.com/package/dotenv">Dotenv package</a>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> type { Config } <span class="hljs-keyword">from</span> <span class="hljs-string">"drizzle-kit"</span>;
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> dotenv <span class="hljs-keyword">from</span> <span class="hljs-string">"dotenv"</span>;

dotenv.config();

<span class="hljs-keyword">if</span> (!process.env.NEON_DATABASE_URL)
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"NEON DATABASE_URL not found in environment"</span>);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
    <span class="hljs-attr">schema</span>: <span class="hljs-string">"./src/app/db/schema.ts"</span>,
    <span class="hljs-attr">out</span>: <span class="hljs-string">"./src/app/db/migrations"</span>,
    <span class="hljs-attr">dialect</span>: <span class="hljs-string">"postgresql"</span>,
    <span class="hljs-attr">dbCredentials</span>: {
        <span class="hljs-attr">url</span>: process.env.NEON_DATABASE_URL,
 },
    <span class="hljs-attr">strict</span>: <span class="hljs-literal">true</span>,
} satisfies Config;
</code></pre>
<p>The <strong>drizzle.config.ts</strong> file contains all the information about your database connection, migration folder, and schema files.</p>
<p>Finally, update the <strong>package.json</strong> file to include the Drizzle Kit commands for generating database migrations and creating the tables.</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>{</span><span><br></span><span> </span><span>"scripts"</span><span> : {</span><span><br></span><span> </span><span>"migrate"</span><span>: </span><span>"npx drizzle-kit generate -- dotenv_config_path='.env.local'"</span><span>,</span><span><br></span><span> </span><span>"db-create"</span><span>: </span><span>"npx drizzle-kit push -- dotenv_config_path='.env.local'"</span><span><br></span><span> }</span><span><br></span><span>}</span></p></td></tr></tbody></table>

<p>You can now run <strong><code>npm run db-create</code></strong> to push the database tables to the Neon console.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXdK9dJHITFXRrqOiK6pFL7hUZtvinCaymedYlOuWu9QUOOEEmKuweQ1z0MflHyhdsffeNJ7HGnFLlm9QQ10rH8q6gwGWB7nr-S6GDyCiHmkNAZCfJNhiwPuBY193H0W9nFLDUeLt8zaethyZ2bU9pMOKO5g?key=QrOqhkDtPIneanOaExEDaA" alt="Neon-tables-dashboard" width="1600" height="941" loading="lazy">
<em>Neon-tables-dashboard</em></p>
<h2 id="heading-creating-the-api-endpoints-for-the-application"><strong>Creating the API Endpoints for the Application</strong></h2>
<p>In the previous section, you created the necessary functions to interact with the database. In this section, you will learn how to create the API endpoints for each database operation.</p>
<p>First, create an <code>api</code> folder within the Next.js app directory. It will contain all the API routes for the application.</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>cd app</span><span><br></span><span>mkdir api</span></p></td></tr></tbody></table>

<p>Add a <strong><code>bank-info</code></strong> folder containing a <strong>route.ts</strong> within the <code>api</code> folder. This means that the API route (<strong>/api/bank-info</strong>) will handle updating and fetching the user’s bank information.</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>cd api</span><span><br></span><span>mkdir bank-info &amp;&amp; cd bank-info</span><span><br></span><span>touch route.ts</span></p></td></tr></tbody></table>

<p>Copy the code snippet below into the /bank-info/route.ts file. The POST request method updates the user’s bank information and returns a response and the GET request method retrieves the bank information from the database using the user’s ID.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { updateBankInfo, getUserBankInfo } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/db/actions"</span>;
<span class="hljs-keyword">import</span> { NextRequest, NextResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/server"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">POST</span>(<span class="hljs-params">req: NextRequest</span>) </span>{
    <span class="hljs-keyword">const</span> { accountName, userID, accountNumber, bankName, currency } = <span class="hljs-keyword">await</span> req.json();
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">await</span> updateBankInfo({
            <span class="hljs-attr">user_id</span>: userID,
            <span class="hljs-attr">bank_name</span>: bankName,
            <span class="hljs-attr">account_number</span>: <span class="hljs-built_in">Number</span>(accountNumber),
            <span class="hljs-attr">account_name</span>: accountName,
            <span class="hljs-attr">currency</span>: currency,
 });
        <span class="hljs-keyword">return</span> NextResponse.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Bank Details Updated!"</span> }, { <span class="hljs-attr">status</span>: <span class="hljs-number">201</span> });
 } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-keyword">return</span> NextResponse.json(
 { <span class="hljs-attr">message</span>: <span class="hljs-string">"An error occurred"</span>, err },
 { <span class="hljs-attr">status</span>: <span class="hljs-number">400</span> }
 );
 }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">GET</span>(<span class="hljs-params">req: NextRequest</span>) </span>{
   <span class="hljs-keyword">const</span> userID  = req.nextUrl.searchParams.get(<span class="hljs-string">"userID"</span>);

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> bankInfo = <span class="hljs-keyword">await</span> getUserBankInfo(userID!);
        <span class="hljs-keyword">return</span> NextResponse.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Fetched bank details"</span>, bankInfo }, { <span class="hljs-attr">status</span>: <span class="hljs-number">200</span> });
 } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-keyword">return</span> NextResponse.json(
 { <span class="hljs-attr">message</span>: <span class="hljs-string">"An error occurred"</span>, err },
 { <span class="hljs-attr">status</span>: <span class="hljs-number">400</span> }
 );
 }
}
</code></pre>
<p>Next, add an <strong>invoice</strong> folder containing a <strong>route.ts</strong> file to the <strong><code>api</code></strong> directory. Copy the code snippet below into the /api/invoice/route.ts file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { createInvoice, getUserInvoices } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/db/actions"</span>;
<span class="hljs-keyword">import</span> { NextRequest, NextResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/server"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">POST</span>(<span class="hljs-params">req: NextRequest</span>) </span>{
    <span class="hljs-keyword">const</span> { customer, title, items, total, ownerID } = <span class="hljs-keyword">await</span> req.json();

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">await</span> createInvoice({
            <span class="hljs-attr">user_id</span>: ownerID,
            <span class="hljs-attr">customer_id</span>: customer,
            title,
            <span class="hljs-attr">total_amount</span>: total,
            <span class="hljs-attr">items</span>: <span class="hljs-built_in">JSON</span>.stringify(items),
 })
        <span class="hljs-keyword">return</span> NextResponse.json(
 { <span class="hljs-attr">message</span>: <span class="hljs-string">"New Invoice Created!"</span> },
 { <span class="hljs-attr">status</span>: <span class="hljs-number">201</span> }
 );
 } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-keyword">return</span> NextResponse.json(
 { <span class="hljs-attr">message</span>: <span class="hljs-string">"An error occurred"</span>, err },
 { <span class="hljs-attr">status</span>: <span class="hljs-number">400</span> }
 );
 }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">GET</span>(<span class="hljs-params">req: NextRequest</span>) </span>{
    <span class="hljs-keyword">const</span> userID = req.nextUrl.searchParams.get(<span class="hljs-string">"userID"</span>);

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> invoices = <span class="hljs-keyword">await</span> getUserInvoices(userID!);
        <span class="hljs-keyword">return</span> NextResponse.json({<span class="hljs-attr">message</span>: <span class="hljs-string">"Invoices retrieved successfully!"</span>, invoices}, { <span class="hljs-attr">status</span>: <span class="hljs-number">200</span> });
 } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-keyword">return</span> NextResponse.json(
 { <span class="hljs-attr">message</span>: <span class="hljs-string">"An error occurred"</span>, err },
 { <span class="hljs-attr">status</span>: <span class="hljs-number">400</span> }
 );
 }
}
</code></pre>
<p>The POST request method creates a new invoice and the GET request method returns all the user’s invoices from the database.</p>
<p>You can also create a sub-folder named <strong><code>single</code></strong> within the <strong>/api/invoices</strong> folder, and add a <strong>route.ts</strong> file within it.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { NextRequest, NextResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/server"</span>;
<span class="hljs-keyword">import</span> { getSingleInvoice } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/db/actions"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">GET</span>(<span class="hljs-params">req: NextRequest</span>) </span>{
   <span class="hljs-keyword">const</span> invoiceID = req.nextUrl.searchParams.get(<span class="hljs-string">"id"</span>);

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> invoice = <span class="hljs-keyword">await</span> getSingleInvoice(invoiceID);
        <span class="hljs-keyword">return</span> NextResponse.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Inovice retrieved successfully!"</span>, invoice }, { <span class="hljs-attr">status</span>: <span class="hljs-number">200</span> });
 } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-keyword">return</span> NextResponse.json(
 { <span class="hljs-attr">message</span>: <span class="hljs-string">"An error occurred"</span>, err },
 { <span class="hljs-attr">status</span>: <span class="hljs-number">400</span> }
 );
 }
}
</code></pre>
<p>The code snippet above accepts an invoice ID and retrieves all its data available within the database table. You can do the same with the <strong>customers</strong> table as well.</p>
<p>Congratulations! You’ve learned how to <em>create</em>, <em>store</em>, and <em>retrieve</em> data from the Neon Postgres database. In the upcoming sections, you’ll uncover how to print and send invoices to customers.</p>
<h2 id="heading-how-to-print-and-download-invoices-in-nextjs"><strong>How to Print and Download Invoices in Next.js</strong></h2>
<p>The <a target="_blank" href="https://www.npmjs.com/package/react-to-print">React-to-print</a> package is a simple JavaScript library that allows you to print the contents of a React component easily without tampering with the component's CSS styles. It converts React components exactly as they are into downloadable PDF files.</p>
<p>First, execute the following code snippet in your terminal to install the package:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>npm install -save react-to-print</span></p></td></tr></tbody></table>

<p>Create a client page (<strong>/invoice/[id].tsx</strong>). </p>
<p>To do this, add an <strong>invoice</strong> folder containing a <strong>[id]</strong> sub-folder to the Next.js app directory. Inside the <strong>[id]</strong> folder, add a <strong>page.tsx</strong> file. This page displays all the information about an invoice and allows users to print, download, and send invoices to customers.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXcnG8Yav_Xnqpmk5lO4PXkKjrWqMzEkOat42mTkGR-bvAEA5VTiZ1nasFEc05H_JR6pwlyars_oWMRuBNg4CCLCNpghvnZUQ8eBen-I0OvdPGYfItoUkcXC-Abz87MjBQdacIFUotw2WGYp7YyJFq6NeOrr?key=QrOqhkDtPIneanOaExEDaA" alt="Invoice-app-download-page-ui" width="1600" height="1022" loading="lazy">
<em>Invoice-app-download-page-ui</em></p>
<p>Create a invoice design similar to the image above by copying the code snippet below into the page.tsx file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> ComponentToPrint = forwardRef&lt;HTMLDivElement, Props&gt;(<span class="hljs-function">(<span class="hljs-params">props, ref</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { id, customer, invoice, bankInfo } = props <span class="hljs-keyword">as</span> Props;

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-full px-2 py-8'</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{ref}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'lg:w-2/3 w-full mx-auto shadow-md border-[1px] rounded min-h-[75vh] p-5'</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">header</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-full flex items-center space-x-4 justify-between'</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-4/5'</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-lg font-semibold mb-3'</span>&gt;</span>INVOICE #0{id}<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'mb-6'</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'opacity-60'</span>&gt;</span>Issuer Name: {bankInfo?.account_name}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'opacity-60'</span>&gt;</span>Date: {formatDateString(invoice?.created_at!)}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-lg font-semibold mb-2'</span>&gt;</span>TO:<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'mb-6'</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'opacity-60'</span>&gt;</span>Name: {invoice?.customer_id}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'opacity-60'</span>&gt;</span>Address: {customer?.address}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'opacity-60'</span>&gt;</span>Email: {customer?.email}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-1/5 flex flex-col'</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'font-extrabold text-2xl'</span>&gt;</span>
              {`${bankInfo?.currency}${Number(invoice?.total_amount).toLocaleString()}`}
            <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-sm opacity-60'</span>&gt;</span>Total Amount<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">header</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'opacity-60'</span>&gt;</span>Subject:<span class="hljs-tag">&lt;/<span class="hljs-name">p</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-lg font-semibold'</span>&gt;</span>{invoice?.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>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">InvoiceTable</span> <span class="hljs-attr">itemList</span>=<span class="hljs-string">{invoice?.items</span> ? <span class="hljs-attr">JSON.parse</span>(<span class="hljs-attr">invoice.items</span>) <span class="hljs-attr">:</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>
  );
});

ComponentToPrint.displayName = <span class="hljs-string">"ComponentToPrint"</span>;
</code></pre>
<p>The code snippet accepts invoice details, including the customer and user’s bank information and renders them within the component.</p>
<p>Finally, you need to wrap this component with another parent one and instruct <strong>React-to-print</strong> to print the sub-component. Add the following code snippet below the <strong><code>ComponentToPrint</code></strong> component.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useReactToPrint } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-to-print"</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">Invoices</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { id } = useParams&lt;{ <span class="hljs-attr">id</span>: string }&gt;();
  <span class="hljs-comment">// Reference to the component to be printed</span>
  <span class="hljs-keyword">const</span> componentRef = useRef&lt;any&gt;();

  <span class="hljs-comment">// States for the data</span>
  <span class="hljs-keyword">const</span> [customer, setCustomer] = useState&lt;Customer&gt;();
  <span class="hljs-keyword">const</span> [bankInfo, setBankInfo] = useState&lt;BankInfo&gt;();
  <span class="hljs-keyword">const</span> [invoice, setInvoice] = useState&lt;Invoice&gt;();

  <span class="hljs-comment">// Function that sends invoice via email</span>
  <span class="hljs-keyword">const</span> handleSendInvoice = <span class="hljs-keyword">async</span> () =&gt; {};

  <span class="hljs-comment">// Function that prints the invoice</span>
  <span class="hljs-keyword">const</span> handlePrint = useReactToPrint({
    <span class="hljs-attr">documentTitle</span>: <span class="hljs-string">"Invoice"</span>,
    <span class="hljs-attr">content</span>: <span class="hljs-function">() =&gt;</span> componentRef.current,
  });

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-full min-h-screen'</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">section</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-full flex p-4 items-center justify-center space-x-5 mb-3'</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">'p-3 text-blue-50 bg-blue-500 rounded-md'</span>
          <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handlePrint}</span>
        &gt;</span>
          Download
        <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">className</span>=<span class="hljs-string">'p-3 text-blue-50 bg-green-500 rounded-md'</span>
          <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> {
            handleSendInvoice();
          }}
        &gt;
          Send Invoice
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">ComponentToPrint</span>
        <span class="hljs-attr">ref</span>=<span class="hljs-string">{componentRef}</span>
        <span class="hljs-attr">id</span>=<span class="hljs-string">{id}</span>
        <span class="hljs-attr">customer</span>=<span class="hljs-string">{customer}</span>
        <span class="hljs-attr">bankInfo</span>=<span class="hljs-string">{bankInfo}</span>
        <span class="hljs-attr">invoice</span>=<span class="hljs-string">{invoice}</span>
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
</code></pre>
<p>The component renders the <strong><code>ComponentToPrint</code></strong> component, creates a reference to it, and prints it using the <a target="_blank" href="https://github.com/MatthewHerbst/react-to-print?tab=readme-ov-file#usage"><strong>useReactToPrint</strong></a> hook.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXeMjZeFBZ_-Y-mP7tH9rmlBYUwSsGIJfOiCQ7VvYOtLhZBJhgZn60bWpFBNlqOWFIGtwMDizCTooXoWtSX6soKbiGr2xKU3PGMC-5YG9wA-9er21DORGzX4IsdtaxoipsQqQVKlGCu7Ix2igPgLEBaWB_I?key=QrOqhkDtPIneanOaExEDaA" alt="Invoice-app-print-ui" width="1600" height="1010" loading="lazy">
<em>Invoice-app-print-ui</em></p>
<h2 id="heading-how-to-send-digital-invoices-with-resend-and-react-email"><strong>How to Send Digital Invoices with Resend and React Email</strong></h2>
<p><a target="_blank" href="https://resend.com/">Resend</a> is an API service that enables us to send and manage emails programmatically, making it easy to integrate email functionality into software applications. </p>
<p><a target="_blank" href="https://react.email/">React Email</a> is a library that allows us to create reusable, beautifully designed email templates using React components. Both packages are created by the person, allowing for smooth integration between the two services.</p>
<p>Install both packages by running the code snippet below:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>npm install resend </span><span><br></span><span>npm install react-email </span><span>@react</span><span>-email/components -E</span></p></td></tr></tbody></table>

<p>Configure React Email by including the following script in your <strong>package.json</strong> file.</p>
<p>The <strong><code>--dir</code></strong> flag gives React Email access to the email templates located within the project. In this case, the email templates are located in the <strong>src/app/emails</strong> folder.</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>{</span><span><br></span><span>&nbsp; &nbsp; </span><span>"scripts"</span><span>: {</span><span><br></span><span>&nbsp; &nbsp; &nbsp; &nbsp; </span><span>"email"</span><span>: </span><span>"email dev --dir src/app/emails"</span><span><br></span><span>&nbsp; &nbsp; }</span><span><br></span><span>}</span></p></td></tr></tbody></table>

<p>Next, create the emails folder containing the email template to be sent to the customers’ email:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Heading, Hr, Text } <span class="hljs-keyword">from</span> <span class="hljs-string">"@react-email/components"</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">EmailTemplate</span>(<span class="hljs-params">{
    invoiceID,
    items,
    amount,
    issuerName,
    accountNumber,
    currency,
}: Props</span>) </span>{
    <span class="hljs-keyword">return</span> (
 <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">Heading</span> <span class="hljs-attr">as</span>=<span class="hljs-string">'h2'</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">color:</span> "#<span class="hljs-attr">0ea5e9</span>" }}&gt;</span>
 Purhcase Invoice from {issuerName}
 <span class="hljs-tag">&lt;/<span class="hljs-name">Heading</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">Text</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginBottom:</span> <span class="hljs-attr">5</span> }}&gt;</span>Invoice No: INV0{invoiceID}<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">Heading</span> <span class="hljs-attr">as</span>=<span class="hljs-string">'h3'</span>&gt;</span> Payment Details:<span class="hljs-tag">&lt;/<span class="hljs-name">Heading</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">Text</span>&gt;</span>Account Details: {issuerName}<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">Text</span>&gt;</span>Account Number: {accountNumber}<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">Text</span>&gt;</span>Total Amount: {`${currency}${amount}`}<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">Hr</span> /&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">Heading</span> <span class="hljs-attr">as</span>=<span class="hljs-string">'h3'</span>&gt;</span> Items: <span class="hljs-tag">&lt;/<span class="hljs-name">Heading</span>&gt;</span>
            {items &amp;&amp;
                items.map((item, index) =&gt; (
 <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">Text</span>&gt;</span>
                            {item.cost} x {item.quantity} = {item.price}
 <span class="hljs-tag">&lt;/<span class="hljs-name">Text</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>
 );
}
</code></pre>
<p>The email template accepts all the invoice details as props and sends a dynamic email template to the user. You can also preview the invoice layout by running <strong><code>npm run email</code></strong> within your terminal.</p>
<p>Next, create a <a target="_blank" href="https://resend.com/docs/introduction">Resend account</a>, and select <strong>API Keys</strong> from the sidebar menu on your dashboard to create one.</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXdTkbkk-f3JIvcGLXoFdeQGpFNF6gDgqZWVL5NnJjcbu17I4dRp3rF8GYNUHXkvF2Gs59OQgjuknTVXWzOjknrJVeZ7xv90LhLZLPeqGgYI-il5PyKEcL3g-E3_VAem-sX13pkRlz-AhqPdgXgVQo884Uce?key=QrOqhkDtPIneanOaExEDaA" alt="resend-api-keys-dashboard" width="1600" height="938" loading="lazy">
<em>resend-api-keys-dashboard</em></p>
<p>Copy the API key into the .env.local file.</p>
<p>Finally, create an API endpoint that accepts the invoice details from the frontend and sends an invoice containing the data to a customer.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { NextRequest, NextResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/server"</span>;
<span class="hljs-keyword">import</span> EmailTemplate <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/emails/email"</span>;
<span class="hljs-keyword">import</span> { Resend } <span class="hljs-keyword">from</span> <span class="hljs-string">"resend"</span>;
<span class="hljs-keyword">const</span> resend = <span class="hljs-keyword">new</span> Resend(process.env.RESEND_API_KEY!);

<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">POST</span>(<span class="hljs-params">req: NextRequest</span>) </span>{
    <span class="hljs-keyword">const</span> {
        invoiceID,
        items,
        title,
        amount,
        customerEmail,
        issuerName,
        accountNumber,
        currency,
 } = <span class="hljs-keyword">await</span> req.json();

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> { data, error } = <span class="hljs-keyword">await</span> resend.emails.send({
            <span class="hljs-attr">from</span>: <span class="hljs-string">"Acme &lt;onboarding@resend.dev&gt;"</span>,
            <span class="hljs-attr">to</span>: [customerEmail],
            <span class="hljs-attr">subject</span>: title,
            <span class="hljs-attr">react</span>: EmailTemplate({
                invoiceID,
                <span class="hljs-attr">items</span>: <span class="hljs-built_in">JSON</span>.parse(items),
                <span class="hljs-attr">amount</span>: <span class="hljs-built_in">Number</span>(amount),
                issuerName,
                accountNumber,
                currency,
 }) <span class="hljs-keyword">as</span> React.ReactElement,
 });

        <span class="hljs-keyword">if</span> (error) {
            <span class="hljs-keyword">return</span> Response.json(
 { <span class="hljs-attr">message</span>: <span class="hljs-string">"Email not sent!"</span>, error },
 { <span class="hljs-attr">status</span>: <span class="hljs-number">500</span> }
 );
 }

        <span class="hljs-keyword">return</span> NextResponse.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Email delivered!"</span> }, { <span class="hljs-attr">status</span>: <span class="hljs-number">200</span> });
 } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-keyword">return</span> NextResponse.json(
 { <span class="hljs-attr">message</span>: <span class="hljs-string">"Email not sent!"</span>, error },
 { <span class="hljs-attr">status</span>: <span class="hljs-number">500</span> }
 );
 }
}
</code></pre>
<p>The code snippet above accepts invoice details from the frontend, passes the required data into the email template, and sends an email to the user.</p>
<h2 id="heading-next-steps"><strong>Next Steps</strong></h2>
<p>Congratulations. By now, you should have a good understanding of how to build full-stack applications with Clerk, Resend, Neon Postgres and Next.js.</p>
<p>If you'd like to learn more about how you can leverage Neon Postgres to build advanced and scalable apps, you can check out the following resources:</p>
<ul>
<li><a target="_blank" href="https://neon.tech/docs/introduction">Neon documentation</a></li>
<li><a target="_blank" href="https://github.com/tyaga001/awesome-neon">Awesome Neon</a></li>
<li><a target="_blank" href="https://github.com/neondatabase/examples">Neon example projects</a></li>
<li><a target="_blank" href="https://neon.tech/docs/guides/vercel">How to integrate Neon with Vercel</a></li>
<li><a target="_blank" href="https://neon.tech/docs/import/import-from-postgres">How to import your data from a Postgres database to Neon</a></li>
</ul>
<h2 id="heading-thank-you-for-reading">Thank you for reading</h2>
<p>If you found this article useful, you can:</p>
<ul>
<li><a target="_blank" href="https://bytesizedbets.com/">Subscribe to my newsletter.</a></li>
<li><a target="_blank" href="https://x.com/TheAnkurTyagi">Follow me on Twitter</a> where I post about my business and writing journey, side projects, and current learnings.</li>
<li>Checkout <a target="_blank" href="https://theankurtyagi.com/">my blog</a> for more tutorials like this on developer tools.   </li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Full Stack Development with Next.js, Clerk, and Neon Postgres ]]>
                </title>
                <description>
                    <![CDATA[ Full stack development is constantly evolving, with new developer tools and products being introduced that allow us to build secure and reliable applications more efficiently. In this tutorial, I’ll walk you through how to build highly performant web... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/nextjs-clerk-neon-fullstack-development/</link>
                <guid isPermaLink="false">66c375561784344f009b632f</guid>
                
                    <category>
                        <![CDATA[ database ]]>
                    </category>
                
                    <category>
                        <![CDATA[ full stack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ankur Tyagi ]]>
                </dc:creator>
                <pubDate>Wed, 10 Jul 2024 15:31:12 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/07/Orange---Yellow-Gradient-Make-Design-Blog-Banner--77-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Full stack development is constantly evolving, with new developer tools and products being introduced that allow us to build secure and reliable applications more efficiently.</p>
<p>In this tutorial, I’ll walk you through how to build highly performant web applications with <a target="_blank" href="https://neon.tech">Neon – a serverless PostgreSQL</a> database designed for the cloud. You'll also learn how to perform CRUD (Create, Read, Update, and Delete) operations with Neon.</p>
<p>By the end of this tutorial, you will have the basic knowledge required to start building advanced and scalable web applications with Neon.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents:</strong></h2>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-neon">What is Neon?</a></li>
<li><a class="post-section-overview" href="#heading-why-neon">Why Neon?</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-neon-to-a-nextjs-app">How to add Neon to a Next.js app</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-neon-serverless-driver-with-drizzle-orm-in-nextjs">How to set up Neon Serverless Driver with Drizzle ORM in Next.js</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-application-interface-with-nextjs">How to Build the Application Interface with Next.js</a></li>
<li><a class="post-section-overview" href="#heading-how-to-authenticate-users-with-clerk">How to Authenticate Users with Clerk</a></li>
<li><a class="post-section-overview" href="#heading-crud-operations-with-the-neon-database">CRUD Operations with the Neon Database</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
<li><a class="post-section-overview" href="#heading-next-steps">Next Steps</a></li>
</ul>
<h2 id="heading-what-is-neon">What is Neon?</h2>
<p><a target="_blank" href="https://github.com/neondatabase/neon">Neon</a> is an open-source, scalable, and efficient Postgres DB that separates compute from storage. This means that database computation processes (queries, transactions, and so on) are handled by one set of resources (compute), while the data itself is stored on a separate set of resources (storage). </p>
<p>This architecture allows for greater scalability and performance, making Neon a solid choice for modern web applications.</p>
<p><img src="https://lh7-us.googleusercontent.com/docsz/AD_4nXcT4hh-liS2uYYcatl8jC6h9gFqArEw113_WaPzoTFxeps_G97JIVhJKVSQq5DC52NJ0GOoQm4sYL5QhyLhC_e_xocDjSp7iks6j8kv6WSnhRzLVy8TxftshzFnwK238QuVsGdnnBpL_nLDmXju3klwlB6T?key=4GdX_KHTwBEvJEyZsT7b3Q" alt="Neon - a serverless Postgres database" width="1600" height="416" loading="lazy">
<em><a target="_blank" href="https://neon.tech">Neon - a serverless Postgre</a>s database</em></p>
<h3 id="heading-3-things-to-remember-about-neon">3 Things to Remember About Neon:</h3>
<ul>
<li>🐘 <strong>Postgres</strong>: Neon is built on the foundation of Postgres. It supports the same extensions, drivers, and SQL syntax as Postgres, ensuring familiarity and ease of use.</li>
<li>☁️ <strong>Serverless</strong>: Neon operates on a serverless model. Your database is represented as a simple URL, and Neon automatically scales up and down based on workload demands. Say goodbye to over-provisioning.</li>
<li>🌱 <strong>Branching</strong>: Just like version control for code, Neon allows you to create instant, isolated copies of your data. This feature is invaluable for development, testing, and maintaining separate environments.</li>
</ul>
<h2 id="heading-why-neon">Why Neon?</h2>
<p>Neon brings the serverless experience to Postgres. Developers can build faster and scale their products effortlessly, without the need to dedicate big teams or big budgets to the database.</p>
<p>Neon supports <a target="_blank" href="https://neon.tech/docs/introduction#framework-and-language-quickstarts">multiple languages and frameworks</a> – but what are the unique features that make Neon stand out?</p>
<h3 id="heading-instant-branching-and-auto-scaling">Instant branching and auto-scaling</h3>
<p><a target="_blank" href="https://neon.tech/blog/why-you-want-a-database-that-scales-to-zero">Neon</a> allows you to <a target="_blank" href="https://neon.tech/branching">create database branches instantly</a> for testing, development, and staging environments. This lets you experiment without affecting the production database. </p>
<p>It also provides an <a target="_blank" href="https://neon.tech/docs/introduction/autoscaling">auto-scaling capability</a> that automatically adjusts resources based on the application's workload, ensuring optimal performance and cost-efficiency.</p>
<p><img src="https://lh7-us.googleusercontent.com/docsz/AD_4nXc1HSzmptXUYsu49JPbWSizwp64G-JVME-7kmmwSKNYLcc1wUmwWXvBa6kuVxncpyazqSPgj_N4ABZddjNG2rDTHE8MFIGm3yKy1DPpiV6C7GZ1tOcTzOFkvtDwpleeJZC--V4efudtYnPe-XKs8K2P740R?key=4GdX_KHTwBEvJEyZsT7b3Q" alt="Neon DB Main Branch Dashboard" width="1600" height="929" loading="lazy">
<em>Neon DB Main Dashboard</em></p>
<h3 id="heading-support-for-ai-applications">Support for AI applications</h3>
<p>Neon <a target="_blank" href="https://neon.tech/ai">supports AI and machine learning applications</a> by providing a high-performance and scalable infrastructure. It enables you to perform semantic and similarity searches in Postgres and handles complex queries and large datasets efficiently, making it ideal for AI or LLM applications.</p>
<h3 id="heading-open-source">Open-source</h3>
<p>Neon is backed by a <a target="_blank" href="https://github.com/neondatabase/neon">vibrant community</a> of Postgres hackers, systems engineers, and cloud engineers who are all huge fans of Postgres. </p>
<p>As an open-source platform, Neon offers transparency and flexibility. You can also reach out to the team and contributors to ask questions, contribute, and help improve the software.</p>
<h3 id="heading-serverless-architecture">Serverless Architecture</h3>
<p><a target="_blank" href="https://neon.tech/blog/architecture-decisions-in-neon">Neon</a> eliminates the need for manual server management, allowing you to focus on building applications rather than maintaining infrastructure. Its serverless nature provides on-demand scalability, ensuring that your application can handle varying loads without manual intervention.</p>
<h3 id="heading-built-upon-postgres">Built upon Postgres</h3>
<p>Postgres is one of the most reliable open-source relational <a target="_blank" href="https://neon.tech/blog/get-page-at-lsn">database</a> systems. Neon inherits all the advanced features, stability, and performance optimizations of Postgres, including support for ACID transactions, advanced SQL, and NoSQL/JSON, to create a cheaper and more efficient database for cloud environments.</p>
<h2 id="heading-how-to-add-neon-to-a-nextjs-app">How to Add Neon to a Next.js App</h2>
<p>Neon supports multiple frameworks and libraries and provides clear and detailed documentation on adding Neon to them. The Neon serverless driver enables us to connect and interact with Neon in a Next.js application.</p>
<p>Before we proceed, let’s <a target="_blank" href="https://neon.tech/docs/guides/nextjs">create a Neon account and project</a>.</p>
<p><img src="https://lh7-us.googleusercontent.com/docsz/AD_4nXeLMMmCdF3yK_mflMt4mqz3woiTLibcrGCMK5-AE1f2KftVKYduH39BybuVdu68G2am8uwDWHJUyisFittXeqCmcgxNyhcZXIiXHjxIIu-eymmM_-VVdAMW0LWTVA7NrXI-QXNEYso3Sj1FrLX0tvSP6yNK?key=4GdX_KHTwBEvJEyZsT7b3Q" alt="Neon DB Projects Overview" width="1600" height="938" loading="lazy">
<em>Neon DB Projects Overview: View and manage all your projects in one place.</em></p>
<p>Within your project dashboard, you'll find a database connection string. You'll use this to interact with your Neon database.</p>
<p><img src="https://lh7-us.googleusercontent.com/docsz/AD_4nXfpIPj10xMleoJEKIMFghRGp2ofgC0zJEA0C2ExMr2ijz673RM_45Kuh1RXVKkEn_uW-hU6-YIPd35C73gYMZtN_7lvChQ4MSK47CIWovh8MyUzRQluguEhAEXdvRZ8wxpOLINIyVfp50u1gIOf3foBAzg3?key=4GdX_KHTwBEvJEyZsT7b3Q" alt="Neon DB Project Dashboard" width="1600" height="897" loading="lazy">
<em>Neon DB Project Dashboard: Manage database settings with ease from the project dashboard.</em></p>
<p>Create a TypeScript Next.js project by running the following code snippet in your terminal:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>npx create-next-app neon-blog-with-clerk</span></p></td></tr></tbody></table>

<p>Next, install the Neon Serverless package:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>npm install @neondatabase/serverless</span></p></td></tr></tbody></table>

<p>Create a .env.local file and copy your database connection string into the file:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>NEON_DATABASE_URL=</span><span>"postgres://&lt;user&gt;:&lt;password&gt;@&lt;endpoint_hostname&gt;.neon.tech:&lt;port&gt;/&lt;dbname&gt;?sslmode=require"</span></p></td></tr></tbody></table>

<p>Create a 'db' folder containing an index.ts file within the Next.js app directory and copy the code snippet below into the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { neon } <span class="hljs-keyword">from</span> <span class="hljs-string">'@neondatabase/serverless'</span>;

<span class="hljs-keyword">if</span> (!process.env.NEON_DATABASE_URL) {
  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'NEON_DATABASE_URL must be a Neon postgres connection string'</span>)
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getDBVersion = <span class="hljs-keyword">async</span>() =&gt; {
    <span class="hljs-keyword">const</span> sql = neon(process.env.NEON_DATABASE_URL!);
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> sql<span class="hljs-string">`SELECT version()`</span>;
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">version</span>: response[<span class="hljs-number">0</span>].version }
}
</code></pre>
<p>Convert the app/page.tsx file to a server component and execute the <code>getDBVersion()</code> function:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { getDBVersion } <span class="hljs-keyword">from</span> <span class="hljs-string">"./db"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> { version } = <span class="hljs-keyword">await</span> getDBVersion();
    <span class="hljs-built_in">console</span>.log({version})

   <span class="hljs-keyword">return</span> (<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{/** — UI elements — */}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>)

}
</code></pre>
<p>The <code>getDBVersion()</code> function establishes a connection with the Neon database and allows us to run SQL queries using the Postgres client. This function returns the database version, which is then logged to the console.</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>{</span><span><br></span><span>&nbsp; version: </span><span>'PostgreSQL 16.3 on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit'</span><span><br></span><span>}</span></p></td></tr></tbody></table>

<p>Congratulations – you’ve successfully added Neon to your Next.js application.</p>
<p>But interacting with the Neon database by writing SQL queries directly can require extra learning or introduce complexities for developers who are not familiar with SQL. It can also lead to errors or performance issues when performing complex queries. </p>
<p>This is why Neon supports database ORMs such as Drizzle ORM, which provide a higher-level interface for interacting with the database. <a target="_blank" href="https://orm.drizzle.team/docs/overview">Drizzle ORM</a> enables you to write complex query functions and interact with the database easily using TypeScript.</p>
<h2 id="heading-how-to-set-up-neon-serverless-driver-with-drizzle-orm-in-nextjs">How to Set Up Neon Serverless Driver with Drizzle ORM in Next.js</h2>
<p><a target="_blank" href="https://orm.drizzle.team/docs/overview">Drizzle ORM</a> lets you query data and perform various operations on the database using simple TypeScript query commands. It is lightweight, typesafe, and easy to use.</p>
<p>First, you'll need to install the <a target="_blank" href="https://orm.drizzle.team/kit-docs/overview">Drizzle Kit</a> and the <a target="_blank" href="https://orm.drizzle.team/docs/overview">Drizzle ORM</a> package.</p>
<p>Drizzle Kit lets you manage the database schema and migrations.</p>
<pre><code class="lang-bash">npm i drizzle-orm
npm i -D drizzle-kit
</code></pre>
<p>Inside the db folder, add an actions.ts, and schema.ts file:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> db
touch actions.ts schema.ts
</code></pre>
<p>Add the code snippet below into the db/schema.ts file. It contains the database schema.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {  text, serial, pgTable, timestamp } <span class="hljs-keyword">from</span> <span class="hljs-string">"drizzle-orm/pg-core"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> postsTable = pgTable(<span class="hljs-string">"posts"</span>, {
    <span class="hljs-attr">id</span>: serial(<span class="hljs-string">"id"</span>).primaryKey().notNull(),
    <span class="hljs-attr">content</span>: text(<span class="hljs-string">"content"</span>).notNull(),
    <span class="hljs-attr">author</span>: text(<span class="hljs-string">"author"</span>).notNull(),
    <span class="hljs-attr">author_id</span>: text(<span class="hljs-string">"author_id"</span>).notNull(),
    <span class="hljs-attr">title</span>: text(<span class="hljs-string">"title"</span>).notNull(),
    <span class="hljs-attr">created_at</span>: timestamp(<span class="hljs-string">"created_at"</span>).defaultNow(),
    <span class="hljs-attr">slug</span>: text(<span class="hljs-string">"slug"</span>).notNull(),
});
</code></pre>
<p>Update the db/index.ts file to connect to the Neon database and export the Drizzle instance (db). This will be used to execute typesafe SQL queries against your Postgres database hosted by Neon.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { neon } <span class="hljs-keyword">from</span> <span class="hljs-string">'@neondatabase/serverless'</span>;
<span class="hljs-keyword">import</span> { drizzle } <span class="hljs-keyword">from</span> <span class="hljs-string">'drizzle-orm/neon-http'</span>;
<span class="hljs-keyword">import</span> { postsTable } <span class="hljs-keyword">from</span> <span class="hljs-string">'./schema'</span>;


<span class="hljs-keyword">if</span> (!process.env.NEON_DATABASE_URL) {
  <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'DATABASE_URL must be a Neon postgres connection string'</span>)
}
<span class="hljs-keyword">const</span> sql = neon(process.env.NEON_DATABASE_URL!);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> db = drizzle(sql, {
  <span class="hljs-attr">schema</span>: { postsTable }
});
</code></pre>
<p>Next, create a drizzle.config.ts file at the root of the Next.js folder and add the following configuration:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> type { Config } <span class="hljs-keyword">from</span> <span class="hljs-string">'drizzle-kit'</span>;
<span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> dotenv <span class="hljs-keyword">from</span> <span class="hljs-string">"dotenv"</span>;

dotenv.config();

<span class="hljs-keyword">if</span> (!process.env.NEON_DATABASE_URL) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'NEON DATABASE_URL not found in environment'</span>);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">schema</span>: <span class="hljs-string">'./src/app/db/schema.ts'</span>,
  <span class="hljs-attr">out</span>: <span class="hljs-string">'./src/app/db/migrations'</span>,
 <span class="hljs-attr">dialect</span>: <span class="hljs-string">"postgresql"</span>,
  <span class="hljs-attr">dbCredentials</span>: {
    <span class="hljs-attr">url</span>: process.env.NEON_DATABASE_URL,
 },
  <span class="hljs-attr">strict</span>: <span class="hljs-literal">true</span>,
} satisfies Config;
</code></pre>
<p>The drizzle.config.ts file contains all the information about your database connection, migration folder, and schema files. </p>
<p>Finally, update the package.json file to include the Drizzle Kit commands for generating database migrations and updating the tables.</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>{</span><span><br></span><span> "scripts" : {</span><span><br></span><span>&nbsp; "migrate": </span><span>"npx drizzle-kit generate -- dotenv_config_path='.env.local'"</span><span>,</span><span><br></span><span>&nbsp; "db-create": </span><span>"npx drizzle-kit push -- dotenv_config_path='.env.local'"</span><span><br></span><span> }</span><span><br></span><span>}</span></p></td></tr></tbody></table>

<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/image-8.png" alt="Neon DB Tables Dashboard" width="600" height="400" loading="lazy">
<em>Neon DB Tables Dashboard: Effortlessly manage your database tables and view all data.</em></p>
<h2 id="heading-how-to-build-the-application-interface-with-nextjs">How to Build the Application Interface with Next.js</h2>
<p>In this section, you’ll learn how to build a blog application that allows users to read posts and authenticate authors, enabling them to create and delete posts from the Neon database. </p>
<p>The application is divided into 3 pages:</p>
<ul>
<li>Home Page: displays all the available blog posts.</li>
<li>Post Details Page (/posts/[slug]): displays the content of a particular blog post.</li>
<li>Create Post Page (/posts/create): allows authors to create new blog posts.</li>
</ul>
<p>Install the following packages:</p>
<pre><code class="lang-javascript">npm install date-fns react-simplemde-editor easymde react-markdown remark-gfm dotenv
</code></pre>
<p>The <a target="_blank" href="https://github.com/date-fns/date-fns">Date Fns package</a> allows us to convert the posts' timestamps to human-readable forms for display within the application. The <a target="_blank" href="https://www.npmjs.com/package/react-simplemde-editor">React SimpleMDE Editor</a> provides a WYSIWYG editor for creating content in markdown formats using an interactive editor, and the <a target="_blank" href="https://www.npmjs.com/package/react-markdown">React Markdown package</a> converts the markdown texts to their corresponding plain formats.</p>
<p>Next, create a utils.ts file within the Next.js app folder and copy the code snippet below into the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { format } <span class="hljs-keyword">from</span> <span class="hljs-string">"date-fns"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> formatDateString = (dateString: <span class="hljs-built_in">Date</span> | <span class="hljs-literal">null</span>): <span class="hljs-function"><span class="hljs-params">string</span> =&gt;</span> {
    <span class="hljs-keyword">if</span> (!dateString) <span class="hljs-keyword">return</span> <span class="hljs-string">""</span>;
    <span class="hljs-keyword">const</span> date = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(dateString);
    <span class="hljs-keyword">const</span> formattedDate = format(date, <span class="hljs-string">"MMMM do yyyy, h:mma"</span>);
    <span class="hljs-keyword">return</span> formattedDate;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> slugifySentences = (sentence: string): <span class="hljs-function"><span class="hljs-params">string</span> =&gt;</span> {
    <span class="hljs-keyword">const</span> slug = sentence
 .toLowerCase()
 .replace(<span class="hljs-regexp">/[^a-z0-9\s-]/g</span>, <span class="hljs-string">""</span>)
 .replace(<span class="hljs-regexp">/\s+/g</span>, <span class="hljs-string">"-"</span>);

    <span class="hljs-comment">// Generate 5 random letters</span>
    <span class="hljs-keyword">const</span> randomLetters = <span class="hljs-built_in">Array</span>.from({ <span class="hljs-attr">length</span>: <span class="hljs-number">5</span> }, <span class="hljs-function">() =&gt;</span>
        <span class="hljs-built_in">String</span>.fromCharCode(<span class="hljs-number">97</span> + <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">26</span>))
 ).join(<span class="hljs-string">""</span>);

    <span class="hljs-keyword">return</span> <span class="hljs-string">`<span class="hljs-subst">${slug}</span>-<span class="hljs-subst">${randomLetters}</span>`</span>;
};
</code></pre>
<p>The <code>formatDateString</code> function accepts a Date object and returns the date and time in a human-readable format using the date-fns package. The <code>slugifySentences</code> function creates a slug for each post using the post's title, which is useful for implementing the routes for each post.</p>
<p>Copy the code snippet below into the app/page.tsx file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">"next/link"</span>;
<span class="hljs-keyword">import</span> { formatDateString, slugifySentences } <span class="hljs-keyword">from</span> <span class="hljs-string">"./utils"</span>;

interface Post {
    <span class="hljs-attr">author_id</span>: string;
    title: string;
    content: string;
    author: string;
    slug: string;
    id: number | <span class="hljs-literal">null</span>;
    created_at: <span class="hljs-built_in">Date</span> | <span class="hljs-literal">null</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// dummy posts</span>
    <span class="hljs-keyword">const</span> posts: Post[] = [
        {
            <span class="hljs-attr">author_id</span>: <span class="hljs-string">"1"</span>,
            <span class="hljs-attr">title</span>: <span class="hljs-string">"Welcome to Neon Tutorial"</span>,
            <span class="hljs-attr">content</span>: <span class="hljs-string">"This is a test post"</span>,
            <span class="hljs-attr">author</span>: <span class="hljs-string">"John Doe"</span>,
            <span class="hljs-attr">slug</span>: slugifySentences(<span class="hljs-string">"Welcome to Neon Tutorial"</span>),
            <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,
            <span class="hljs-attr">created_at</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(),
        },
        {
            <span class="hljs-attr">author_id</span>: <span class="hljs-string">"1"</span>,
            <span class="hljs-attr">title</span>: <span class="hljs-string">"Hello World"</span>,
            <span class="hljs-attr">content</span>: <span class="hljs-string">"This is a test post"</span>,
            <span class="hljs-attr">author</span>: <span class="hljs-string">"Jane Doe"</span>,
            <span class="hljs-attr">slug</span>: slugifySentences(<span class="hljs-string">"Hello World"</span>),
            <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>,
            <span class="hljs-attr">created_at</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(),
        },
    ];

    <span class="hljs-comment">// shorten posts with longer title</span>
    <span class="hljs-keyword">const</span> shortenText = (text: string): <span class="hljs-function"><span class="hljs-params">string</span> =&gt;</span> {
        <span class="hljs-keyword">return</span> text.length &lt;= <span class="hljs-number">55</span> ? text : text.slice(<span class="hljs-number">0</span>, <span class="hljs-number">55</span>) + <span class="hljs-string">"..."</span>;
    };

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'md:px-8 py-8 px-4 w-full bg-white'</span>&gt;</span>
                {posts?.map((post) =&gt; (
                    <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                        <span class="hljs-attr">href</span>=<span class="hljs-string">{</span>`/<span class="hljs-attr">posts</span>/${<span class="hljs-attr">post.slug</span>}`}
                        <span class="hljs-attr">className</span>=<span class="hljs-string">'rounded w-full border-[1px] p-4 text-blue-500 hover:bg-blue-50 hover:drop-shadow-md transition-all duration-200 ease-in-out flex items-center justify-between gap-4 mb-4'</span>
                        <span class="hljs-attr">key</span>=<span class="hljs-string">{post.id}</span>
                    &gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-lg font-semibold'</span>&gt;</span>{shortenText(post.title)}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex items-center justify-between'</span>&gt;</span>
                            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-xs text-gray-500'</span>&gt;</span>
                                {formatDateString(post?.created_at)}
                            <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">Link</span>&gt;</span>
                ))}
            <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}
</code></pre>
<p>The app/page.tsx file represents the home page of the application and displays all the available posts.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/image-5.png" alt="blog-app" width="600" height="400" loading="lazy">
<em>It's live - see the power of serverless PostgreSQL and Next.js</em></p>
<p>Next, add the routes for creating posts and reading the contents of each post. Within the Next.js app folder, create a posts directory containing /posts/create and /posts/[slug] subdirectories.</p>
<p>Create a page.tsx file within the /posts/create folder and copy the code snippet below into the file:</p>
<pre><code class="lang-javascript">use client<span class="hljs-string">";
import { useState, useCallback } from "</span>react<span class="hljs-string">";
import { useRouter } from "</span>next/navigation<span class="hljs-string">";
import SimpleMDE from "</span>react-simplemde-editor<span class="hljs-string">";
import "</span>easymde/dist/easymde.min.css<span class="hljs-string">";
import { slugifySentences } from "</span>@/app/utils<span class="hljs-string">";

export default function PostCreate() {
    const [publishing, setPublishing] = useState&lt;boolean&gt;(false);
    const [content, setContent] = useState&lt;string&gt;("</span><span class="hljs-string">");
    const [title, setTitle] = useState&lt;string&gt;("</span><span class="hljs-string">");
    const router = useRouter();

    const onChangeContent = useCallback((value: string) =&gt; {
        setContent(value);
    }, []);

    const handleCreatePost = async (e: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {
        e.preventDefault();
        console.log({ title, content });
        router.push("</span>/<span class="hljs-string">");
    };

    return (
        &lt;div className='min-h-[100vh]'&gt;
            &lt;main className='md:px-8 py-8 px-4 w-full'&gt;
                &lt;form className='flex flex-col w-full' onSubmit={handleCreatePost}&gt;
                    &lt;label htmlFor='title' className='text-sm text-blue-600'&gt;
                        Title
                    &lt;/label&gt;
                    &lt;input
                        type='text'
                        name='title'
                        id='title'
                        value={title}
                        required
                        onChange={(e) =&gt; setTitle(e.target.value)}
                        className='px-4 py-3 border-2 rounded-md text-lg mb-4'
                    /&gt;

                    &lt;label htmlFor='content' className='text-sm text-blue-600'&gt;
                        Content
                    &lt;/label&gt;
                    &lt;SimpleMDE value={content} onChange={onChangeContent} id='content' /&gt;

                    &lt;button
                        type='submit'
                        disabled={publishing}
                        className='bg-blue-600 mt-2 text-white py-3 rounded-md'
                    &gt;
                        {publishing ? "</span>Publishing....please wait<span class="hljs-string">" : "</span>Publish Post<span class="hljs-string">"}
                    &lt;/button&gt;
                &lt;/form&gt;
            &lt;/main&gt;
        &lt;/div&gt;
    );
}</span>
</code></pre>
<p>The /posts/create page renders a form that accepts the title and content of the post, allowing authors to create new blog posts.</p>
<p><img src="https://lh7-us.googleusercontent.com/docsz/AD_4nXed0zewJjjPH6pDp2z4E1dZ-F2d717R5LJuqqrqC-8pzv_PAHcNWem1q_19NgnpUoM9hgBa7MhHtFs0fn_kaEMQOXaRHGPPdoIi1qiOovdTbiPSPlut1EX3Yo-2APt3wvwW08K2BrjJ6rx-R3EDEdxfyNwu?key=4GdX_KHTwBEvJEyZsT7b3Q" alt="How to Create a Post in Blog App" width="1600" height="836" loading="lazy">
<em>Create your next blog post with ease</em></p>
<p>Finally, update the /posts/[slug] page to display each post's content and include a button that allows only the posts' authors to delete posts. (You'll learn how to implement this later in the tutorial.)</p>
<pre><code class="lang-javascript">use client<span class="hljs-string">";
import { useRouter, useParams } from "</span>next/navigation<span class="hljs-string">";
import ReactMarkdown from "</span>react-markdown<span class="hljs-string">";
import { useEffect, useState, useCallback } from "</span>react<span class="hljs-string">";
import remarkGfm from "</span>remark-gfm<span class="hljs-string">";
import { formatDateString } from "</span>@/app/utils<span class="hljs-string">";

export default function Post() {
    const router = useRouter();
    const [loading, setLoading] = useState&lt;boolean&gt;(true);
    const [post, setPost] = useState&lt;Post | null&gt;(null);
    const params = useParams&lt;{ slug: string }&gt;();

    const deletePost = async () =&gt; {
        if (confirm("</span>Are you sure you want to <span class="hljs-keyword">delete</span> <span class="hljs-built_in">this</span> post?<span class="hljs-string">")) {
            alert(`Delete ${params.slug}`);
            router.push("</span>/<span class="hljs-string">");
        }
    };

    return (
        &lt;div&gt;
            &lt;main className='w-full md:px-8 px-4'&gt;
                &lt;header className='mb-6 py-4'&gt;
                    &lt;div className='flex items-center justify-between mb-2'&gt;
                        &lt;h2 className='text-3xl text-blue-700 font-bold'&gt;{post?.title}&lt;/h2&gt;

                        &lt;div className='flex items-center'&gt;
                            &lt;button
                                className='px-4 py-2 rounded text-xs bg-red-200 hover:bg-red-40 mr-3'
                                onClick={() =&gt; deletePost()}
                            &gt;
                                Delete
                            &lt;/button&gt;
                        &lt;/div&gt;
                    &lt;/div&gt;

                    &lt;div className='flex'&gt;
                        &lt;p className='text-red-500 mr-8 text-sm'&gt;
                            Author: &lt;span className='text-gray-700'&gt;{post?.author}&lt;/span&gt;
                        &lt;/p&gt;
                        &lt;p className='text-red-500 mr-6 text-sm'&gt;
                            Posted on:{"</span> <span class="hljs-string">"}
                            &lt;span className='text-gray-700'&gt;
                                {formatDateString(post?.created_at!)}
                            &lt;/span&gt;
                        &lt;/p&gt;
                    &lt;/div&gt;
                &lt;/header&gt;

                &lt;div className='text-sm text-justify'&gt;
                    &lt;ReactMarkdown remarkPlugins={[remarkGfm]}&gt;
                        {post?.content!}
                    &lt;/ReactMarkdown&gt;
                &lt;/div&gt;
            &lt;/main&gt;
        &lt;/div&gt;
    );
}</span>
</code></pre>
<p>The /posts/[slug] page accepts the unique slug for each blog post, fetches the post's content, and allows post authors to delete their own posts.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/image-4.png" alt="Blog Post " width="600" height="400" loading="lazy">
<em>Blog Post</em></p>
<p>Congratulations! You've completed the user interface for the application.</p>
<h2 id="heading-how-to-authenticate-users-with-clerk">How to Authenticate Users with Clerk</h2>
<p><a target="_blank" href="https://github.com/clerkinc">Clerk</a> is a complete user management platform that enables you to add various forms of authentication to your software applications. It provides easy-to-use, flexible UI components and APIs that can be integrated seamlessly into your application.</p>
<p>Install the <a target="_blank" href="https://clerk.com/docs/quickstarts/nextjs">Clerk Next.js SDK</a> by running the following code snippet in your terminal.</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>npm install @clerk/nextjs</span></p></td></tr></tbody></table>

<p>Create a middleware.ts file within the Next.js src folder and copy the code snippet below into the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { clerkMiddleware, createRouteMatcher } <span class="hljs-keyword">from</span> <span class="hljs-string">"@clerk/nextjs/server"</span>;


<span class="hljs-comment">// the createRouteMatcher function accepts an array of routes to be protected</span>
<span class="hljs-keyword">const</span> protectedRoutes = createRouteMatcher([<span class="hljs-string">"/posts/create"</span>]);

<span class="hljs-comment">// protects the route</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> clerkMiddleware(<span class="hljs-function">(<span class="hljs-params">auth, req</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (protectedRoutes(req)) {
        auth().protect();
 }
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> config = {
    <span class="hljs-attr">matcher</span>: [<span class="hljs-string">"/((?!.*\\..*|_next).*)"</span>, <span class="hljs-string">"/"</span>, <span class="hljs-string">"/(api|trpc)(.*)"</span>],
};
</code></pre>
<p>The <code>createRouteMatcher</code> function accepts an array containing routes to be protected from unauthenticated users and the <code>clerkMiddleware()</code> function ensures the routes are protected.</p>
<p>Next, import the following Clerk components into the app/layout.tsx file and update the RootLayout function as shown below:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {
    ClerkProvider,
    SignInButton,
    SignedIn,
    SignedOut,
    UserButton,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@clerk/nextjs"</span>;
<span class="hljs-keyword">import</span> Link <span class="hljs-keyword">from</span> <span class="hljs-string">"next/link"</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">RootLayout</span>(<span class="hljs-params">{
    children,
}: {
    children: React.ReactNode;
}</span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ClerkProvider</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">body</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{inter.className}</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'w-full py-4 border-b-[1px] md:px-8 px-4 text-center flex items-center justify-between sticky top-0 bg-white z-10 '</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">'/'</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'text-xl font-extrabold text-blue-700'</span>&gt;</span>
                            Neon Blog
                        <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>

                        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">'flex items-center gap-5'</span>&gt;</span>
                            {/*-- if user is signed out --*/}
                            <span class="hljs-tag">&lt;<span class="hljs-name">SignedOut</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">SignInButton</span> <span class="hljs-attr">mode</span>=<span class="hljs-string">'modal'</span> /&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">SignedOut</span>&gt;</span>
                            {/*-- if user is signed in --*/}
                            <span class="hljs-tag">&lt;<span class="hljs-name">SignedIn</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">'/posts/create'</span> <span class="hljs-attr">className</span>=<span class="hljs-string">''</span>&gt;</span>
                                    Create Post
                                <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
                                <span class="hljs-tag">&lt;<span class="hljs-name">UserButton</span> <span class="hljs-attr">showName</span> /&gt;</span>
                            <span class="hljs-tag">&lt;/<span class="hljs-name">SignedIn</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>

                    {children}
                <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>
        <span class="hljs-tag">&lt;/<span class="hljs-name">ClerkProvider</span>&gt;</span></span>
    );
}
</code></pre>
<p>When a user is not signed in, the <a target="_blank" href="https://clerk.com/docs/components/unstyled/sign-in-button">Sign in button</a> component is rendered. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/image-3.png" alt="Clerk UI" width="600" height="400" loading="lazy">
<em>Seamless sign-ups redefined with Clerk UI</em></p>
<p>Then, after signing into the application, the Clerk <a target="_blank" href="https://clerk.com/docs/components/user/user-button">User Button component</a> and a link to create a new post are displayed.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/image-17.png" alt="Clerk's User Button component" width="600" height="400" loading="lazy">
<em>After sign-in: Use Clerk's User Button to create a new post</em></p>
<p>Next, create a <a target="_blank" href="https://clerk.com/">Clerk account</a> and add a new application project.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/image-7.png" alt="Clerk's sleek UI dashboard" width="600" height="400" loading="lazy">
<em>Clerk's sleek UI dashboard</em></p>
<p>Select username as the authentication method and create the Clerk project.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/07/image-6.png" alt="clerk-dashboard" width="600" height="400" loading="lazy">
<em>Clerk's sleek UI dashboard</em></p>
<p>Finally, add your Clerk publishable and secret keys into the .env.local file.</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=&lt;your_publishable_key&gt;</span><span><br></span><span>CLERK_SECRET_KEY=&lt;your_secret_key&gt;</span></p></td></tr></tbody></table>

<p>Clerk provides various ways to <a target="_blank" href="https://clerk.com/docs/references/nextjs/read-session-data">read user's data</a> on the client and the server, which is essential for identifying users within the application.</p>
<h2 id="heading-crud-operations-with-the-neon-database">CRUD Operations with the Neon Database</h2>
<p>In this section, you’ll learn how to perform CRUD (Create, Read, Update, Delete) operations with the Neon database. These fundamental operations are essential for interacting with and managing data within any application. </p>
<p>The db/actions.ts file will contain the CRUD operations. Add the following code snippet to the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { db } <span class="hljs-keyword">from</span> <span class="hljs-string">"."</span>;
<span class="hljs-keyword">import</span> { postsTable } <span class="hljs-keyword">from</span> <span class="hljs-string">'./schema'</span>;
<span class="hljs-keyword">import</span> { desc, eq } <span class="hljs-keyword">from</span> <span class="hljs-string">"drizzle-orm"</span>;

<span class="hljs-comment">// add a new row to the posts table</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> createPost = <span class="hljs-keyword">async</span> (post: Post) =&gt; {
    <span class="hljs-keyword">await</span> db.insert(postsTable).values({
        <span class="hljs-attr">content</span>: post.content,
        <span class="hljs-attr">author</span>: post.author,
        <span class="hljs-attr">author_id</span>: post.author_id,
        <span class="hljs-attr">title</span>: post.title,
        <span class="hljs-attr">slug</span>: post.slug,
    });
};

<span class="hljs-comment">// get all the posts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getAllPosts = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> db.select().from(postsTable).orderBy(desc(postsTable.created_at));
};

<span class="hljs-comment">// get a post using its slug</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getSinglePost = <span class="hljs-keyword">async</span> (slug: string) =&gt; {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> db.query.postsTable.findFirst({
        <span class="hljs-attr">where</span>: <span class="hljs-function">(<span class="hljs-params">post, { eq }</span>) =&gt;</span> eq(post.slug, slug)
    });
};

<span class="hljs-comment">// delete a post</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> deletePost = <span class="hljs-keyword">async</span> (id: number) =&gt; {
    <span class="hljs-keyword">await</span> db.delete(postsTable).where(eq(postsTable.id, id));
};

<span class="hljs-comment">// update a post's content</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> updatePost = <span class="hljs-keyword">async</span> (content: string, <span class="hljs-attr">id</span>: number) =&gt; {
    <span class="hljs-keyword">await</span> db.update(postsTable)
        .set({ <span class="hljs-attr">content</span>: content })
        .where(eq(postsTable.id, id));
};
</code></pre>
<p>From the code snippet above:</p>
<ul>
<li>This <code>createPost</code> function takes a post object as an argument and inserts a new row into the <code>postsTable</code> with the specified post content, author, author ID, title, and slug.</li>
<li>The <code>getAllPosts</code> function retrieves all the posts from the <code>postsTable</code> and sorts them in descending order by their creation date (created_at).</li>
<li>This <code>getSinglePost</code> function takes a slug as an argument and retrieves the first post that matches the given slug from the <code>postsTable</code>. The slug is unique, so it will return a single object.</li>
<li>This <code>deletePost</code> function takes an id as an argument and deletes the post with the matching ID from the <code>postsTable</code>.</li>
<li>This <code>updatePost</code> function accepts <code>content</code> and a post's <code>id</code> as arguments and updates the post's content with the matching ID in the <code>postsTable</code>.</li>
</ul>
<p>Finally, you can execute the CRUD functions on the server via API endpoints or <a target="_blank" href="https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#fetching-data-on-the-server-with-fetch">Next.js server fetch requests</a>. </p>
<p>For instance, you can fetch all the existing blog posts within the Neon database and display them within the application using the Next.js server data fetching method:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { getAllPosts } <span class="hljs-keyword">from</span> <span class="hljs-string">"./db/actions"</span>;

<span class="hljs-keyword">const</span> getPosts = <span class="hljs-keyword">async</span> () =&gt; <span class="hljs-keyword">await</span> getAllPosts()

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> posts = <span class="hljs-keyword">await</span> getPosts()

    <span class="hljs-keyword">return</span> (<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{/** -- UI elements --*/}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>)
}
</code></pre>
<p>You can also create a Next.js API endpoint that returns all the available blog posts. Create a /api/posts/all endpoint that returns the posts:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { getPosts } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/app/db/actions"</span>;
<span class="hljs-keyword">import</span> { NextRequest, NextResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">"next/server"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">POST</span>(<span class="hljs-params"></span>) </span>{


    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> getPosts()
        <span class="hljs-keyword">return</span> NextResponse.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"Post fetched"</span>, data }, { <span class="hljs-attr">status</span>: <span class="hljs-number">200</span> });
 } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-keyword">return</span> NextResponse.json(
 { <span class="hljs-attr">message</span>: <span class="hljs-string">"Post not available"</span>, err },
 { <span class="hljs-attr">status</span>: <span class="hljs-number">400</span> }
 );
 }
}
</code></pre>
<p>Congratulations! You’ve completed the project for this tutorial.</p>
<p>You can find the code for the app we built <a target="_blank" href="https://github.com/tyaga001/serverless-postgres-nextjs-handbook">here</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you’ve learned what a Neon database is, how to create one, and how to perform CRUD operations with Neon and Drizzle ORM in a Next.js application.</p>
<p>Neon's serverless architecture, combined with its scalability and performance optimizations, makes it an excellent choice for modern web applications. Neon also provides a smooth developer experience and a community of passionate individuals ready to help you achieve your application goals. Thank you for reading.</p>
<h2 id="heading-next-steps">Next Steps</h2>
<p>By now, you should have a good understanding of how to build full-stack applications with Neon and Next.js.</p>
<p>If you'd like to learn more about how you can leverage Neon to build advanced and scalable applications, you can check out the following resources:</p>
<ul>
<li><a target="_blank" href="https://neon.tech/docs/introduction">Neon documentation</a></li>
<li><a target="_blank" href="https://github.com/neondatabase/examples">Neon example projects</a></li>
<li><a target="_blank" href="https://neon.tech/docs/guides/vercel">How to integrate Neon with Vercel</a></li>
<li><a target="_blank" href="https://neon.tech/docs/import/import-from-postgres">How to import your data from a Postgres database to Neon</a></li>
</ul>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/j4Vak4J10KU" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-thanks-for-reading"><strong>Thanks for Reading!</strong></h2>
<p>That's it for this tutorial. I hope you learned something new today.</p>
<p>If you did, please share so that it reaches others as well.</p>
<p>You can connect with me on <a target="_blank" href="https://twitter.com/TheAnkurTyagi">Twitter</a> or subscribe to my <a target="_blank" href="https://bytesizedbets.com/">newsletter</a>. </p>
<h3 id="heading-want-to-read-more-interesting-blog-posts"><strong>Want to read more interesting blog posts?</strong></h3>
<p>You can read more tutorials like this one on my <strong><a target="_blank" href="https://theankurtyagi.com/">blog</a>.</strong></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a RAG Chatbot with Agent Cloud and Google Sheets ]]>
                </title>
                <description>
                    <![CDATA[ Today's companies are data factories. Every interaction, transaction, and internal process generates a constant stream of information. This data holds immense value, promising to improve decision-making, streamline operations, and unlock deep custome... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-rag-chatbot-agent-cloud-google-sheets/</link>
                <guid isPermaLink="false">66c3754b1e62f88108dcb76b</guid>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ LLM&#39;s  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ankur Tyagi ]]>
                </dc:creator>
                <pubDate>Wed, 26 Jun 2024 14:43:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/05/Orange---Yellow-Gradient-Make-Design-Blog-Banner--73-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Today's companies are data factories. Every interaction, transaction, and internal process generates a constant stream of information.</p>
<p>This data holds immense value, promising to improve decision-making, streamline operations, and unlock deep customer insights. </p>
<p>But data often remains siloed and inaccessible. It may be spread across different departments and systems, and it can be challenging to understand and utilize effectively.</p>
<p>This is where the concept of Retrieval-Augmented Generation (<a target="_blank" href="https://blogs.nvidia.com/blog/what-is-retrieval-augmented-generation/">RAG</a>) technology comes in. By combining the power of retrieval-based techniques and modern generative AI tools, you can build Retrieval-Augmented Generation (RAG) chat applications that allow you to interact with your data using a simple chat interface. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/image-57.png" alt="Conceptual flow of using RAG with LLMs." width="600" height="400" loading="lazy">
<em>What is Retrieval-Augmented Generation (RAG)?</em></p>
<p>But before you can chat about your data, a lot of “legwork” is involved. Setting up the infrastructure – the pipeline, vector database, message broker, and knowledge retrieval – is a complex and time-consuming process. This is where the open source tool <a target="_blank" href="https://theankurtyagi.com/what-is-agent-cloud/">Agent Cloud</a> comes in.</p>
<p>In this guide, you'll learn all about Agent Cloud and what it can do. We'll start by looking at some background info and the current problems we're dealing with. Then, we'll see how Agent Cloud can help solve them.</p>
<h2 id="heading-how-i-started-working-with-agent-cloud">How I Started Working with Agent Cloud</h2>
<p>I'm passionate about new technology and <a target="_blank" href="https://theankurtyagi.com/blog/">developer tools</a>, and I sit somewhere between Product Marketing, Growth, and Developer Advocacy. I specialize in the creation of high-quality, technical written content for educational purposes. </p>
<p>I've been involved with the web for ~14 years, the last 4 of which have been documented in punishing detail on my <a target="_blank" href="https://theankurtyagi.com/">website</a>.</p>
<p>I liked being a Software Engineer but, what I really <strong>love</strong> to do is code, design, develop, and then write.</p>
<p>Earlier this year I met <a target="_blank" href="https://www.linkedin.com/in/andrewnada/">Andrew</a> (founder of Agent cloud) in a private Slack group. He was seeking someone who could not only write about the product but also discuss and teach people about what they're building. I reached out to him, and after two rounds of discussions, we began working together.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/image-35.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I started with building some cool RAG chatbots in my local and later wrote a couple of comprehensive guides on "<a target="_blank" href="https://www.agentcloud.dev/blog">How to Build a RAG Chatbot with Agent Cloud</a>".</p>
<p>In this article, I'll teach you how to build a RAG chat app using Agent Cloud to privately and securely talk with your Google Sheets data. I'll also talk about why I think Agent Cloud is good open source developer tool.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-agent-cloud">What is Agent Cloud</a>?</li>
<li><a class="post-section-overview" href="#heading-what-is-retrieval-augmented-generation">What is RAG</a>?</li>
<li><a class="post-section-overview" href="#heading-challenges-of-building-a-rag-chatbot-without-agent-cloud">Challenges of Building a RAG Chatbot Without Agent Cloud</a></li>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-agent-cloud-via-docker">How to Set Up Agent Cloud via Docker</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-models-in-agent-cloud">How to Add Models in Agent Cloud</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-your-gcp-service-account-key">How to Create Your GCP Service Account Key</a></li>
<li><a class="post-section-overview" href="#heading-how-to-enable-google-sheets-api">How to Enable Google Sheets API</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-your-data-sources">How to Set Up your Data Sources</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-tools">How to Set Up tools</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-an-agent">How to Set Up an Agent</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-task">How to Create a Task</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-your-app">How to Set Up your App</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-what-is-agent-cloud">What is Agent Cloud?</h2>
<p><a target="_blank" href="https://www.agentcloud.dev">Agent Cloud</a> is an open-source platform that lets you build private, secure chat applications powered by large language models (think ChatGPT). </p>
<p>It streamlines this process by providing a "RAG as a service" offering and a built-in pipeline that allows you to split, chunk, and embed data from over 300 sources (including <a target="_blank" href="https://www.agentcloud.dev/integrations/google-sheets">Google Sheets</a>, Salesforce, Atlassian Confluence, <a target="_blank" href="https://dev.to/agentcloud/how-to-build-a-rag-chat-app-with-agent-cloud-and-bigquery-15b">BigQuery</a>, <a target="_blank" href="https://dev.to/agentcloud/how-to-build-a-rag-chatbot-with-agentcloud-and-mongodb-4la7">MongoDB</a>, <a target="_blank" href="https://dev.to/agentcloud/how-to-build-a-chat-app-with-your-postgres-data-using-agent-cloud-33hk">Postgres Data</a>, SharePoint, and OneDrive).</p>
<p><img src="https://lh7-us.googleusercontent.com/mCVaA9lJyTTTLY7YNebA8AyR5Tj_iQ3werHlAERD9-NgHPQ6BXUo42NMIm9HwnIXni-iWaTrjVtROtmx8XhY7RXF_wh2LnYAifRDnP7GYFl9EAvP83EuEtoHa7BM4OZBjCokVzYwBF-4Nrd8TlG-JvQ" alt="List of data sources agentcloud supports" width="1600" height="809" loading="lazy">
<em>Data sources</em></p>
<h2 id="heading-what-is-retrieval-augmented-generation">What is Retrieval-Augmented Generation?</h2>
<p>RAG is a process for enhancing the accuracy of large language models. It does this through the on-demand retrieval of external data and by injecting context into the prompt, at runtime. </p>
<p>This data can come from various sources, such as your customers' documentation/web pages (through scraping), and data or documents from dozens (if not hundreds) of 3rd party applications like their Databases, Google BigQuery, HubSpot, Google Ads, Google Analytics 4 (GA4) and so on.</p>
<p>For those who want to dive deeper into Retrieval-Augmented Generation and understand its broader applications and significance, I highly recommend reading this comprehensive <a target="_blank" href="https://blogs.nvidia.com/blog/what-is-retrieval-augmented-generation/">blog by NVIDIA</a>. It offers valuable insights and context that complement the practical aspects covered in this article.</p>
<h2 id="heading-challenges-of-building-a-rag-chatbot-without-agent-cloud">Challenges of Building a RAG Chatbot Without Agent Cloud</h2>
<p>If you're working with these AI tools on a daily basis, it becomes easy to understand the value they bring and realize the significance of Agent Cloud in simplifying the chatbot development process. </p>
<p>But to fully appreciate its benefits, you should understand how chatbot development was handled before such tools were available.</p>
<p>Before tools like Agent Cloud, creating a RAG (Retrieval-Augmented Generation) chatbot was a daunting and resource-intensive task. You had to manually integrate various components, which required significant expertise in multiple areas. </p>
<p>Here are some challenges faced and the solutions the Agent Cloud team had to devise:</p>
<h3 id="heading-data-retrieval-and-management">Data Retrieval and Management:</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/image-36.png" alt="Image" width="600" height="400" loading="lazy">
<em>List of data sources</em></p>
<ul>
<li><strong>Problem:</strong> Ensuring that the chatbot could efficiently retrieve and manage data from sources like Google Sheets, Databases, and so on. </li>
<li><strong>Without Agent Cloud:</strong> Developers had to write custom scripts for data retrieval, often using APIs to fetch data from Google Sheets. This involved handling data formatting, error checking, and real-time updates manually. It was a time-consuming process prone to errors.</li>
<li><strong>Agent Cloud's Solution:</strong> Automates data retrieval and management, ensuring seamless and accurate integration with minimal manual intervention.</li>
</ul>
<h3 id="heading-natural-language-processing-nlp">Natural Language Processing (NLP):</h3>
<ul>
<li><strong>Problem:</strong> Implementing NLP to understand user queries and generate human-like responses.</li>
<li><strong>Without Agent Cloud:</strong> Developers needed to integrate standalone NLP engines such as TensorFlow. This required training models on vast datasets, fine-tuning them for accuracy, and constantly updating them to handle new queries effectively.</li>
<li><strong>Agent Cloud's Solution:</strong> Offers built-in NLP capabilities, significantly reducing setup time and providing high-quality language understanding out of the box.</li>
</ul>
<h3 id="heading-scalability-and-maintenance">Scalability and Maintenance:</h3>
<ul>
<li><strong>Problem:</strong> Ensuring the chatbot could handle increasing data loads and user interactions.</li>
<li><strong>Without Agent Cloud:</strong> Building a scalable architecture meant investing in robust server infrastructure, writing efficient code, and continuously monitoring and maintaining the system to handle growth.</li>
<li><strong>Agent Cloud's Solution:</strong> Designed to scale effortlessly, allowing developers to focus on improving functionality rather than managing infrastructure.</li>
</ul>
<h3 id="heading-user-interaction-and-experience">User Interaction and Experience:</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/image-37.png" alt="Image" width="600" height="400" loading="lazy">
<em>Agent cloud app UI</em></p>
<ul>
<li><strong>Problem:</strong> Creating an engaging and user-friendly interface.</li>
<li><strong>Without Agent Cloud:</strong> Developers had to build custom interfaces, often from scratch, which required additional design and development resources. Ensuring smooth interactions and responsiveness was a major challenge.</li>
<li><strong>Agent Cloud's Solution:</strong> Provides pre-built templates and easy integration options, enhancing the user experience with minimal effort.</li>
</ul>
<p>By understanding these challenges, you can see how a tool like Agent Cloud helps the process of building RAG chatbots. It addresses the pain points of manual data handling, complex NLP integration, scalability issues, and user interface design, providing an all-in-one solution that saves time and resources.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>You don't need any prior knowledge of RAG chat apps or Google Sheets to follow along because Agent Cloud handles the data splitting, encoding, storage, and synchronization. This allows you to focus on talking to your data and interpreting the results.</p>
<h2 id="heading-how-to-set-up-agent-cloud-via-docker">How to Set Up Agent Cloud via Docker</h2>
<p>First, you'll need to install Docker on your system if you don't have it already. <a target="_blank" href="https://docs.docker.com/get-docker/">Docker</a> is a platform for running containerized applications.</p>
<p>Open your terminal and run the following command to clone the Agent Cloud repository from GitHub:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>git</span><span> clonehttps://github.com/rnadigital/agentcloud.git</span></p></td></tr></tbody></table>

<p>Use the following command to move into the newly cloned <code>agentcloud</code> directory:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>cd</span><span> agentcloud</span></p></td></tr></tbody></table>

<p>To run Agent Cloud locally, execute this command:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>chmod</span><span> +x install.sh &amp;&amp; ./install.sh</span></p></td></tr></tbody></table>

<p>This command will grant executable permissions to the install.sh script and then run it. The script will download the necessary Docker images and start the Agent Cloud containers within your Docker environment.</p>
<p><img src="https://lh7-us.googleusercontent.com/IJP_WeswIONKA5EsL87jVisv0mZRsk__P5BajAlXZU3fQW8Fif6mdjqW0t-NTCkU_ZNHAk6PJ4U5UthUmDFOsOQhnmQyY6HwMxHEDIxfqy-VfZODKOq7jFv9OpAlXkR1AszdYK0gkn0RDEut3Y7U7K4" alt="Image" width="1587" height="1600" loading="lazy">
<em>local dev setup- agent cloud</em></p>
<p>Once the installation script finishes successfully, you can view the running Agent Cloud containers in the Docker application.</p>
<p><img src="https://lh7-us.googleusercontent.com/oZ4mwbfNiCtFcv4scaILguo5QYVR_cwU5mpJqEzDzq-2gMHtyrD-XbZJnMiloPDFmVcFaUc6KQLyBWw6SnnSlVrTU-IcBIspkIELSZaGJ3M-9bRaq7H9NX94tdug39a98p0XQfa3RNmCYsxiSlTQDUA" alt="Image" width="1600" height="536" loading="lazy">
<em>local docker services</em></p>
<p>Once everything is up and running, you can access the Agent Cloud user interface in your web browser. </p>
<p>Navigate to the URL:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>http://localhost:3000/register</span></p></td></tr></tbody></table>

<p>This will typically open the registration page where you can create an account to use Agent Cloud. </p>
<p><img src="https://lh7-us.googleusercontent.com/-o54n5I5Z_6RByvP6IaDXyDC5hlLgkRMFCEHlvJukZ5RWMV31G0ty2NZC09xA-O2-wslq_BUWCxGMcWRX1RT-ed5D75MFqOvNZR5-qA1Dg_lDChV-BGnrdNgq4epGxGGWmgSfT0qLvxq8J80tgAG64Q" alt="Image" width="1049" height="1600" loading="lazy">
<em>signup- page- agent cloud</em></p>
<p>You can now log in using the credentials you created during signup.</p>
<p>Once you've successfully registered and logged in, you'll be greeted by the Agent Cloud user interface. This interface provides a central hub for managing your data sources, agents, tasks, models, applications, credentials, and so on.</p>
<p><img src="https://lh7-us.googleusercontent.com/zEAo52ay_80MFLRDZPeRUgMCgx0VtPhOzX_68BSkO0Bkh9-66sAtrVYRTig15imqVFTAHs6OZ0fijXYrZxUMgeExMkRFyTEI9OvKijZWBZKzaQcYrBl0dfJ-MH5E5_5G-IpcTs-312lIdq77INYDk00" alt="Image" width="1600" height="780" loading="lazy">
<em>Home screen - agent cloud</em></p>
<p>Congratulations! You've successfully set up Agent Cloud. Now let's move on to the next step and build our RAG chat application using Google Sheets as the data source.</p>
<h2 id="heading-how-to-add-models-in-agent-cloud">How to Add Models in Agent Cloud</h2>
<p>Go to the Models tab in Agent Cloud. Click the Add Models button to add two types of models. </p>
<ul>
<li>A fast embed model is a lightweight model that runs locally on your machine. It splits and chunks your data before embedding it.</li>
<li>OpenAI is a popular cloud-based LLM provider.</li>
</ul>
<p><img src="https://lh7-us.googleusercontent.com/SlXyhi9xFjz8o1dsMnNApxNDJ8G-NEppj6jfP1TkyaNjU3X6Ewt5NuKQ4mj9SKYxsQOMJ650ErJVvJWR9w4WbNfPRtb26pXnjzoUEsOX6jN7foDbiaM_U6jUJE9HSKqQpSDK54QKjLyI_T9yr2xTGDs" alt="Image" width="1600" height="348" loading="lazy">
<em>Models screen- agent cloud</em></p>
<h2 id="heading-how-to-create-your-gcp-service-account-key">How to Create Your GCP Service Account Key</h2>
<p>Agent Cloud offers two authentication methods for accessing your Google Sheets data:</p>
<ul>
<li>Google (Auth) – This method involves directly authorizing Agent Cloud through your Google account.</li>
<li>Service Key Account – This approach utilizes a service key, a credential specifically created for application access to Google Cloud Platform (GCP) services, including Google Sheets.</li>
</ul>
<p>For this guide, I'll focus on the service key account method, which is generally considered a more secure and streamlined approach for application-to-application communication.</p>
<p><strong>Here is what you’ll need:</strong></p>
<ul>
<li>A Google Cloud Platform (GCP) project with the Google Sheets API enabled.</li>
<li>A service account key is created within your GCP project. This key will be used to authenticate Agent Cloud.</li>
</ul>
<p>I'll walk you through creating a service account key in your GCP project and configuring Agent Cloud to use it to access your Google Sheets data.</p>
<p>First, create a Google Cloud Platform account. Then create a new project. You can give your project any name that you prefer.</p>
<p><img src="https://lh7-us.googleusercontent.com/s1m6tovJn9Hv7NsWNat4-0AKU_PzwiO6oujqSFwG0Yj-lEyVFbwBMrNIWd-h6ill46ZbHqmdrBH8_xTxXWRP-I6G33n2qB9jhYqCNvtQiYqmc15rSJ7jgP9Qw4y4CfaWDkHfNVl_cb4qHa5HhfyTnns" alt="Image" width="1600" height="892" loading="lazy">
<em>How to Create a GCP Account</em></p>
<p>Navigate to the "IAM &amp; Admin" section and select "Service Accounts."</p>
<p><img src="https://lh7-us.googleusercontent.com/jObPCuwTVS1B9-z5yy4rX4Xi775Ur2AGz8B8k-dISs92F-0Ww5Nk4i3m77VzwvKT5w8pjtpHvEBqfvPlKQf6HC_hF4ghh6mQmeAj_BQm7qH3DJeF_tUFZU2lrDvZ5jB3taEpmQu5kn4cWM_W8mWbA_M" alt="Image" width="1600" height="687" loading="lazy">
<em>IAM and Admin Screen</em></p>
<p>Click "Create Service Account" and provide a name and description.</p>
<p><img src="https://lh7-us.googleusercontent.com/un89_I_sIaEsLrROHvoZF10CYb0KeOrRjhzcm_kregQD-4v7-7Tg0xkhVOqqTNwPcaE0xvio_SL9OD4JFxxwql_T_YIbazfAUADcwh-tkM8FZ8YNwiDiEgTqA-zELb4kIILxFYGDw6t4Vc8qYD7UFPI" alt="Image" width="1600" height="656" loading="lazy">
<em>How to add service account</em></p>
<p>Enter your service account name and unique service ID, then click <strong>Done</strong>.</p>
<p><img src="https://lh7-us.googleusercontent.com/pKIZ1n1-dTCXB0Lzqb5unx-ChrghpEVp8z_zC0Cv6N5PhN2oaHKtgBjsutM_YvynvtSO17cq89uIB6koyktx0W-vxGy_xIyr_nOlwDe_jcvU4ZtU1fGKDG7lMxPvgMZgjkgMO6odWV56gR3BAKWq53I" alt="Image" width="1464" height="1286" loading="lazy">
<em>How to create a service account in GCP</em></p>
<p>Under the actions tab, click the manage keys option.</p>
<p><img src="https://lh7-us.googleusercontent.com/ju2mGTR2qMExBbW1vt8budeR1MeA9uNZddtx-IJhFAUaV-bw0GlVJUny9kdXJWndgWA4VXpl70DtYMpXCKQj7-zLus_3iJkl430EoIIcNtBfe2FThCFwQirwlCb0YJwJHb5z54HVKbuw4WlC8PY-HyE" alt="Image" width="1600" height="598" loading="lazy">
<em>Project</em></p>
<p>Click the <strong>ADD KEY</strong> button and select the Create Key option.</p>
<p>Select JSON as the key type, then click on Create New Key. </p>
<p><img src="https://lh7-us.googleusercontent.com/cynXfWX4n8ngRB-mB_CTLvUC1xA4ZqmSnaYtv6K3haSgJyAGlTk-l__J2LXZhupGJpJcZ9LW6NDlw6l05YlKev6sYVsXC9vMjCD3LgPGcCX9O405L-Ixn-LV8jmJbiRcd97y2TfL3zvezgZMi4eNYKw" alt="Image" width="1600" height="855" loading="lazy">
<em>Add keys in GCP</em></p>
<p>Select JSON as the key type, then click on Create. Your service account JSON or key JSON will automatically download.</p>
<p><img src="https://lh7-us.googleusercontent.com/RmAJh50XGOSlSbK-hqWveHc4GozeFZSTsB3oU7y7d4nJ-tiEbfdcyQDdrUOfJonS-8w2GAw30vF1AlII8SOEPHSGz7Ip2Xdc60ypSi_gfljQJa_w84UoyfakUM_U-DcbcOlujk4uN0rrFDXG19aW0Rc" alt="Image" width="1600" height="820" loading="lazy">
<em>Create private key in GCP</em></p>
<p>Keep this file safe we will use it later for authentication. </p>
<h2 id="heading-how-to-enable-google-sheets-api">How to Enable Google Sheets API</h2>
<p>You'll also need to enable the Google Sheets API in your Google Cloud project.</p>
<p>Open the left navigation menu and navigate to "APIs &amp; Services" and then "Library."</p>
<p><img src="https://lh7-us.googleusercontent.com/1wVRoIvsYl6cYlaxeck2Ob2EviXaktCRdI68xVMjwSbXfABsYWTAkCHNEW7kwc2Ww2MoBop8-3-vS9s_1FIGuM7lq1E5cmp02dZ4ApPdbasZ6SneopXV5PGaiGF5AnVUVV9LVTdMQW8qSpOCzzfMs40" alt="Image" width="1600" height="1022" loading="lazy">
<em>APIs and Services in GCP</em></p>
<p>Type "Google Sheets API" in the search bar and press Enter.</p>
<p>The Google Sheets API will appear in the search results. Click on it.</p>
<p><img src="https://lh7-us.googleusercontent.com/TUq6HeWJfhZkZaAHQRst9cZKKlG3zbQkU8NBfl5tEZ43pgBbyYPDiLpHUt9yu1xF1-e8XWUp2isXS7zRcYonVlOwoEj2KJn2PZbK65UmA1KWv2y9AcoCxIaTuCaq5pJ2rrCo1wiZbwL1nDnBhsjPzec" alt="Image" width="1600" height="642" loading="lazy">
<em>Google Sheets API</em></p>
<p>On the API details page, click the "<strong>ENABLE</strong>" button.</p>
<p><img src="https://lh7-us.googleusercontent.com/Rh0qrd79Yqlw1YF5JZ_CSJwvO8ZpnytUs0392Y3OBEmmpLar1JyuOggQo-qms4qWtv9ZPLS69uiNmn4Hi4fSneAQZRIR-eRxWtbagqwNrf0q5qNGIxKJNAnjNk1uDziRLlzny4ZWZL0zeljWkmFT-7E" alt="Image" width="1600" height="879" loading="lazy">
<em>Enable- Google Sheets API</em></p>
<h2 id="heading-how-to-set-up-your-data-sources">How to Set Up your Data Sources</h2>
<p>Agent Cloud empowers you to process data from a wide range of sources. </p>
<p>In this guide, I'll use Google Sheets as the data source. Sheets is a popular web-based spreadsheet application included in Google Workspace. Google Sheets allows you to create, edit, and collaborate on spreadsheets in real-time. </p>
<p>For this example, I'll be working with a financial consumer complaints dataset stored in a Google Sheet. </p>
<p>This dataset contains several columns representing key sales stages and details, potentially including:</p>
<ul>
<li>Complaint ID</li>
<li>Submitted Via</li>
<li>Date Submitted</li>
<li>Date Received</li>
<li>State</li>
<li>Product</li>
<li>Sub-product</li>
<li>Issue</li>
<li>Sub-issue</li>
<li>Company</li>
<li>Public Response</li>
<li>Company Response to Consumer</li>
<li>Timely Response?</li>
</ul>
<p>Here's how to connect your Google Sheets data:</p>
<p>Navigate to the Data Sources tab within the Agent Cloud interface.</p>
<p>Click the button labeled <strong>New Connection</strong>. This will initiate the process of adding a new data source.</p>
<p><img src="https://lh7-us.googleusercontent.com/Tr8WlGVlJTmef0xrTvAxjLeTHCpLua_VDxZ9jamnlXQDY8wKsPf0skYQ_b1PFE0d9K13ndHS-piLpDKu16ikxLL9-AUb4lpgFw2pA6-TOI0lRwLQR5KBPbus5FDqJNkbKQYXdf2s4daLRHP4gPci0Ec" alt="Image" width="1600" height="756" loading="lazy">
<em>Data Sources- Screen- Agent Cloud</em></p>
<p>Search and select “Google sheets” as the data source.</p>
<p><img src="https://lh7-us.googleusercontent.com/aNI6G2gH3HNF4XqibSxNOHjXWwW7X_jgtyK79B0k46LJUL1j_1G9vgYOtUdgT__6mjet3AMXEosqRLsrXZHZsf2rW3UhKPKdpAO2v6RFl8acpdINe6l6-1Y4ZGP52oP4xQVrm1XBlpHVuZVYLfx6-M0" alt="Image" width="1600" height="732" loading="lazy">
<em>Create a Datasource</em></p>
<p>I've named our data source <strong>Financial Consumer Complaints</strong>, which you can name however you want. Add a clear and concise description. The data sync will be manual. This means you'll need to initiate the data refresh process whenever you want the latest information from your Google Sheets to be reflected in Agent Cloud.</p>
<p>Enter the appropriate row batch size. Row batch size means how many rows are processed from the Google sheet. For example, the default value 200 would process rows 1-201, then 201-401, and so on. </p>
<p>Choose the authentication method as “<strong>Service Account Key Authentication</strong>”.</p>
<p>Enter the JSON key <strong>we downloaded earlier above</strong> under the Service Account Information field.</p>
<p>Enter the link to the Google spreadsheet you want to sync. To copy the link, click the 'Share' button in the top-right corner of the spreadsheet, then click 'Copy link'.</p>
<p><img src="https://lh7-us.googleusercontent.com/L9rtuBctsZISgx997WPk-zW25t46yJEvTMg7-wOCE7yBYPrd6l58BkPRFSWIErEf-1QR8v_6QHWQMtOlMyjOVfPPUUiE6yTOSg5BV5DexE3Jw_fANfmPSQRQqueAYJ3ODS3HRFzmA19PN8JYtOTXbi4" alt="Image" width="1600" height="1576" loading="lazy">
<em>Adding details under datasource</em></p>
<p>Click the submit button.</p>
<p>Select the collection and fields you want to sync to the vector database and enter their descriptions.</p>
<p><img src="https://lh7-us.googleusercontent.com/oPDKJbdi5uWh4q0staVJA4gTUytt_EVxCPBeIhV8VUGBsShtYeH9OJ_1R1uvN6HJYcgBvX64DHKt4qxNggdV6bx0filBtMdsuDo_xyhJjmipgIO52dzNmy-ABAtOwi-x-l7hCJoo6lFITMOpSDz83eo" alt="Image" width="1600" height="607" loading="lazy">
<em>Model pop-up</em></p>
<p>Click continue and Choose the field you want to embed then choose the model. </p>
<p><img src="https://lh7-us.googleusercontent.com/fa8oHY4rDm9SHQ4La-FM5c8BuWq1eWOsTzAhyq-IgMBaGIXR5crLog7gQ3Ziq0X_cngVy5J9yCF6Ld8u-Py6ByQI_S72jB4a5An8BHES6lng2675hjQeyP-ai0_zBY5pmTmX1LgRDI1qLPGsQ3Ws0QA" alt="Image" width="1600" height="649" loading="lazy">
<em>Data sources</em></p>
<p>The connection to your Google Sheet is now established. </p>
<p>During the initial run, Agent Cloud will process your spreadsheet data and convert it into a format suitable for efficient retrieval. This processed data will then be stored within Agent Cloud's vector store.</p>
<p><img src="https://lh7-us.googleusercontent.com/DWPrunB5TnUXQg7E0gV_XwoETIGO-Qo7cBpN8oYUl9Up--j3_roKiNS6--3CZZADnSeQWfjtO-j3r9RfQsVxQBsZ_NZvhq5Ahnkik2fGPaz0B6bDDVUJRq5rVXDxEkZ0fIIxMxRjqRdxZOIAM3SXiug" alt="Image" width="1600" height="353" loading="lazy">
<em>New- datasource- screen</em></p>
<p>If you're comfortable with technical details, you can verify the data's presence in the Qdrant vector store running locally on port 6333. </p>
<p>This can be accessed through: http://localhost:6333/dashboard#/collections.</p>
<p>Look for a new collection corresponding to your Google Sheet data.</p>
<p><img src="https://lh7-us.googleusercontent.com/gEdkROWowqm1Xr8Q0ixb65CVZB-UNrzLnNh6KPXkPhAZaP5vHIOSJMeK28nyXye2I846SbMhfo9qG9I2qp67r6BNJLHJDM_z9kYc-KSoN0bUpfUS0CIoQBV_qQcVeXm5mqbIxclnM4VCN4pmc_w1u1k" alt="Image" width="1600" height="817" loading="lazy">
<em>collection-screen</em></p>
<p>You can click the collection to view the payloads and the fields populated in the collection.</p>
<p><img src="https://lh7-us.googleusercontent.com/kQtXRqdF1VR_4UmJUqYGcNMcoVrx9kj1Ncrkgvgb3nzEX7s8UvHscOkYEx18P1qxYy6U04UjLDz-SqjVtTsvjHjxajx5qwsAsVkRPXy1nFBAtjELcht8cSL1vE4jVl49J39KHZuIohiNkAFscO9H4m0" alt="Image" width="1600" height="1070" loading="lazy">
<em>Payload</em></p>
<h2 id="heading-how-to-set-up-tools">How to Set Up Tools</h2>
<p>Tools play a crucial role in facilitating effective interaction between the AI agent and its environment, enabling seamless information processing and action execution aligned with its objectives. </p>
<p>These tools can include functions, APIs, data sources, and resources tailored to empower the agent to autonomously and efficiently undertake diverse tasks. While you have the flexibility to craft your own tools as per their requirements, Agent Cloud also streamlines the process by automatically generating a tool upon the addition of a new data source, </p>
<p>Click the Tools tab tools to switch to the Tools page and click the <strong>New</strong> button.</p>
<p>Enter the tools' names and add a description. A verbose and detailed description helps agents better understand when to use each tool.</p>
<p>Choose the data source and click the Save button.</p>
<p><img src="https://lh7-us.googleusercontent.com/URmNamQ6ccOn5FbABAaBo7tfSjAmxZZgT_sN5W8aNrzdfFPbMCMcRwUk6X5YNL_CKTctP-cQxUURBwCADnGQyALwEUmmoQgBBUAsdtEDUWkWxH9oRp15pKHFcnObNpCkjw_eEdSiGnZbdVZodNraQ5Y" alt="Image" width="1600" height="543" loading="lazy">
<em>Edit tool screen</em></p>
<h2 id="heading-how-to-set-up-an-agent">How to Set Up an Agent</h2>
<p>AI Agents are intelligent systems that excel at handling routine or repetitive tasks by perceiving their environment, collecting data, and using that data to make decisions. </p>
<p>Click the Agents tab and then click on the New Agent button.</p>
<p><img src="https://lh7-us.googleusercontent.com/5JrsomIfl-WKZ2hOLW-MZ34xhRpJdhx9hzKwAz2Ab0kDtCVTnZy_rsgjoreGuS533ZCgq125n11M9siNy_AlFHTuMXOHhCSkFGzbE6gNSCQ7YMxw4-Sut1f_-ydDeKchOJjeAtcKxUVoSBmattKAiIg" alt="Image" width="1600" height="691" loading="lazy">
<em>New Agent Screen</em></p>
<p>Define an Agent's <strong>Name</strong>, <strong>Role</strong>, <strong>Goal</strong>, and <strong>Backstory</strong> as shown below.</p>
<p>In the "Model" section, choose the AI engine to power your agent's reasoning capabilities.  For this example, we've selected  "OpenAI GPT-4" as both the Model and Function Calling Model.</p>
<p>Choose the tool we set up earlier under Tools (Optional).</p>
<p><img src="https://lh7-us.googleusercontent.com/d_NajepTkSjuk-tkpiuWcdupeY02HU5nYatqyfjMgj6WXXvBJTgYuStRoSgCFAqNpU4WK8MIarqLJY5MvytjkuQWI4CFyBvTvDsylYEAWgKS8p9f-EbO6LvHYnRFwBd5yQjDvt6XGlriuEfGkicVi-g" alt="Image" width="1119" height="1600" loading="lazy">
<em>Adding Details in Agent Screen</em></p>
<p>If "OpenAI GPT-4" isn't already configured within Agent Cloud, you can easily add it. Click on the "Model" field and select "Add new model." A new window will appear, allowing you to define the model's name, type, credentials (your OpenAI API key), and the specific LLM model.  </p>
<p><img src="https://lh7-us.googleusercontent.com/0M9gkgwNfR-2VPGdgVN75Aqh-_aZuotKjasIVbiuOEaV6Wf8O-rQZo3bwk8-ZdHv8tfgRywXnlSy57An1rndCfzmfExx4K7nZ_UXpqDOJ_x1q0IUEnYaMRWqI6EzoEWcDzwx4CsFeWncc2bIiI82uOA" alt="Image" width="1600" height="1114" loading="lazy">
<em>Model- popup</em></p>
<p>Click the Save button. Once you save this information, "GPT-4" will be available for future agent creation.</p>
<p><img src="https://lh7-us.googleusercontent.com/3qE96774xn2RHAecevtTlCQ4GUv_WcC6r3sFZFdPNGUNCafENkdQ5SaONMSLzwwIsFyjIgLyp-XlVU-ZijHBybv_ay2KCICrxcR71w7GgqCuklvXK5znfIoRtM03Wd9pSdXFUEWVKnrYnjaGv__l8kk" alt="Image" width="1600" height="846" loading="lazy">
<em>Agents-Screen</em></p>
<h2 id="heading-how-to-create-a-task">How to Create a Task</h2>
<p>Tasks are specific actions assigned to agents for completion. To create a new task, navigate to the Tasks tab and click the "Add Task" button.</p>
<p><img src="https://lh7-us.googleusercontent.com/wN1Wy93IPaxiU6NdElwizuJ4e6meqz4-jMiibnKN02WZqNikxMPWOZKsWDNVSlGU-X7IpY1cqPPAlF13aVKSVBGyw-ImPGKYXH2XH-1Yjg3FjI66mBrU-_hrPfbTntKJ1JZ4keZ5X06jnK06-ikwNdE" alt="Image" width="1600" height="622" loading="lazy">
<em>Create Tasks Screen</em></p>
<p>Enter the following details in the below pop-up window. </p>
<ul>
<li><strong>Task name</strong>: Give your task a clear and concise title that reflects its purpose.</li>
<li><strong>Detailed description</strong> of what the task entails.</li>
<li>Choose the tool we created earlier, which is “<strong>Financial Consumer Complaint</strong>” in my case.</li>
<li>Select the preferred Agent we created earlier we named it “<strong>Customer Complaints Agent</strong>”</li>
</ul>
<p><img src="https://lh7-us.googleusercontent.com/RujrqL0gjUYk31aJSYKQYyY55T_nIy-PdSdm8uhcodHGcs8lsHyctuJLnkI4COAL34tXHFjnsjRvypzRaThbIkVEhlTU2quvmaA5cxihN_MHGfg6QaB1i7oBgtXx2Bwa5hEQLbdtEM8Za70JgLRuONI" alt="Image" width="1298" height="1298" loading="lazy">
<em>Adding Details in Tasks Screen</em></p>
<p>Click the <strong>Save</strong> button to save the task.</p>
<h2 id="heading-how-to-set-up-your-app">How to Set Up Your App</h2>
<p>Now, buckle up because we're about to embark on the exciting part: creating a conversational app. This app will transform our data source into a chat partner, allowing you to have interactive conversations and unlock insights through natural language.</p>
<p>So far, we've laid the foundation for our app. We've created:</p>
<ul>
<li>A data source.</li>
<li>A data retrieval tool.</li>
<li>An Agent.</li>
<li>Tasks.</li>
</ul>
<p>Click the Apps tab and then click the <strong>New App</strong> Button, and then enter the details below:</p>
<p><img src="https://lh7-us.googleusercontent.com/jBOcimMYi1-HQGbpUXJYFLho-5LGKGKVJJ5E9OnNpy83PzSX0RINP0DN6oK_9p9LztvSm5yQdMSDqmLqY_fvmC_3pREpO2f9h_zRmPGwaYdr9TwLmcWzWhZSRPMlWDK15x9cEdMz6gNwyL4uYnB0Pyc" alt="Image" width="1600" height="1056" loading="lazy">
<em>New App Screen</em></p>
<ul>
<li>App name – Enter the name you’d like to give to your app.</li>
<li>Enter a description of what our App.</li>
<li>Select a Tasks</li>
<li>Select an Agent</li>
<li>Choose a Chat Manager Model – Pick the Open AI model we configured earlier.</li>
</ul>
<p><img src="https://lh7-us.googleusercontent.com/1SMFU6auHKUZgxZGUQ9yJQzKuqDwtU4KbZBJGweDPaJBW_Oc6plMrcror2o2dWindixOWu6bGyYNGg6eUB5TS2iYYmOzP9FHFIvacZTle32ZqL7Gylxxy1XlZLR9YivE3KDHBXxJ1_dvO-aVHWbc2Mo" alt="Image" width="1600" height="745" loading="lazy">
<em>App Screen</em></p>
<p>Now, let's test our app. </p>
<p>Clicking the play button will open a chat window for us where we can have a conversation.</p>
<p><img src="https://lh7-us.googleusercontent.com/ju-0a5CRhaHgVEqFZkDhdlPkCBqtwOM0Mjnsaz2D7ftl5Hsfku49pkBFYyY9DBr41Rzbwqm90vf8pirBzz2hsUBnsM6YOwjPoCmGhzkm6OQefP_jNNIdOGKx9geEibQLQv_ZzGsaN8AhuXIBl-F9P64" alt="Image" width="1600" height="785" loading="lazy">
<em>Play Button in App Screen</em></p>
<p>A chat interface window should be opened for us where we can chat with our data. For instance, with the data I have used, I can prompt the app to summarize some of the issues raised by customers related to the Mortgage product.</p>
<p><img src="https://lh7-us.googleusercontent.com/tfEGreNRfDRwyiJs0ZomrjfRmSQajuMSYzqL-uxa8tULoso0d56mwE-JiNpLEiv0-x0dnN9XgpajdiwW9aa-dvfnF47dmhW4daQkJ21JYYmC25svAfd3PBXdm9HrmC6tVFAlZ04-H0PLf1B5GfOsqHc" alt="Image" width="1600" height="777" loading="lazy">
<em>Live Chat with Data</em></p>
<p>Or I can have it summarize company responses related to different issues.</p>
<p><img src="https://lh7-us.googleusercontent.com/KpGoDKLffAOcUzFdN7_tJwMved1uDklnP5XQAkaLbwhn1hTlnOgI7Br8QmaPfGUgaK8hLvW958KIyZfDI577PB2aTHgSEHQjBFRN7TeOAgQA0cYjds4R5evGbuMfNXBJOl5x-7Uur0akvOsWgNjI4V4" alt="Image" width="1600" height="802" loading="lazy">
<em>Answer Screen</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, we explored building a RAG chat application using Agent Cloud and Google Sheets. </p>
<p>We covered setting up Google Sheets as a data source, embedding the data for efficient retrieval, and storing it within a vector store like Qdrant. We also learned how to create tools for Agents (chatbot components) and build an app chat interface where users can interact with the data without writing a single line of code.</p>
<p>If you want to read more interesting articles about developer tools, React, Next.js, AI and more, then I'll encourage you to checkout my <a target="_blank" href="https://theankurtyagi.com/">blog</a>. </p>
<p>Some of the fresh articles I've written this year. </p>
<ul>
<li><a target="_blank" href="https://theankurtyagi.com/how-to-create-blog-with-nextjs-and-firebase/">How I Build a Blog with Next.js and Firebase</a></li>
<li><a target="_blank" href="https://theankurtyagi.com/appwrite/">How I Build a Task Management App with React and Appwrite</a></li>
<li><a target="_blank" href="https://theankurtyagi.com/notes-app-react-supabase/">How I Build a Notes App Using React and Supabase – The Complete Guide</a></li>
<li><a target="_blank" href="https://dev.to/agentcloud/how-to-build-a-rag-chat-app-with-agent-cloud-and-bigquery-15b">How I Build a RAG Chat App With Agent Cloud and BigQuery</a></li>
</ul>
<p>You can get in touch if you have any questions or corrections. I’m expecting them.</p>
<p>And if you found this tutorial useful, please share it with your friends and colleagues who might benefit from it as well. Your support enables me to continue producing useful content for the tech community.</p>
<p>Now it’s time to take the next step by subscribing to my <strong><a target="_blank" href="https://bytesizedbets.com/">newsletter</a></strong> and following me on <a target="_blank" href="https://twitter.com/theankurtyagi"><strong>Twitter</strong></a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn React – A Guide to the Key Concepts ]]>
                </title>
                <description>
                    <![CDATA[ Welcome to this comprehensive guide to learning React. If you're looking for an efficient and well-thought-out approach to understanding React fundamentals, you've just hit the jackpot. This guide follows the 80/20 rule – we'll focus on a few key Rea... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-react-key-concepts/</link>
                <guid isPermaLink="false">66c3755363ac6ce6ab8eba86</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ankur Tyagi ]]>
                </dc:creator>
                <pubDate>Sat, 06 Jan 2024 01:52:22 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/12/Orange---Yellow-Gradient-Make-Design-Blog-Banner--56-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Welcome to this comprehensive guide to learning React. If you're looking for an efficient and well-thought-out approach to understanding React fundamentals, you've just hit the jackpot.</p>
<p>This guide follows the 80/20 rule – we'll focus on a few key React concepts that you'll use in the majority of your work with React.</p>
<p>I created this resource for beginner and intermediate developers. In it, I’ll cover all the basic concepts you need to know to get started with React.</p>
<p>Are you ready to build your first React app? Let’s get started.</p>
<p>By the time you finish this guide, you should have a solid understanding of React basics, including:</p>
<ul>
<li><strong><a class="post-section-overview" href="#heading-what-is-react">What is React?</a></strong></li>
<li><a class="post-section-overview" href="#heading-how-much-javascript-do-you-need-to-know-before-learning-react"><strong>How Much JavaScript Do You Need to Know Before Learning React?</strong></a></li>
<li><strong><a class="post-section-overview" href="#heading-why-learn-react">Why</a> </strong>Learn React?<em>**</em></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-react"><strong>How to Set Up React</strong></a></li>
<li><a class="post-section-overview" href="#heading-key-concepts-to-understand-in-react"><strong>Key Concepts to Understand in React</strong></a></li>
<li><strong><a class="post-section-overview" href="#heading-what-is-jsx">What is JSX?</a></strong></li>
<li><strong><a class="post-section-overview" href="#heading-what-is-react-state">What is React State?</a></strong></li>
<li><strong><a class="post-section-overview" href="#heading-props-in-react">Props in React</a></strong></li>
<li><strong><a class="post-section-overview" href="#heading-how-to-display-list-items-in-react">How to display list items in React</a></strong></li>
<li><strong><a class="post-section-overview" href="#heading-react-event-handlers">React Event Handlers</a></strong></li>
<li><strong><a class="post-section-overview" href="#heading-react-hooks">React Hooks</a></strong></li>
<li><strong><a class="post-section-overview" href="#heading-data-flow-in-react">Data Flow in React</a></strong></li>
</ul>
<p>These topics will form the foundation for all advanced concepts you'll learn later on. </p>
<p>This guide is a great resource for JavaScript programmers who are starting to learn React.</p>
<h2 id="heading-what-is-react">What is React?</h2>
<p>React is a JavaScript library for building web app user interfaces.</p>
<p>It's open-source and developed by Facebook. With React, you can create a quick and scalable web app by breaking the UI down into smaller components.</p>
<p>React adopts a declarative style, but what does that mean? You might have come across the terms imperative and declarative in coding discussions, so let's break it down.</p>
<p>In imperative coding, you instruct the computer on what steps to take to achieve the result.</p>
<p><strong>Here's an example:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> num = [<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-keyword">let</span> tripled  = [];
<span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; num.length; i++){
    <span class="hljs-keyword">let</span> newNum = num[I] * <span class="hljs-number">3</span>;
    tripled.push(newNum)
}
<span class="hljs-built_in">console</span>.log(tripled)   <span class="hljs-comment">// [ 3, 6, 9, 12 ]</span>
</code></pre>
<p>In the code above, we give step-by-step instructions to carry out certain tasks. If our instructions are incorrect, the machine might not deliver the expected outcome. It only does what we clearly instruct it to do.</p>
<p>On the flip side, in declarative programming, we state our desired outcome, and the computer works out the steps to achieve it.</p>
<p><strong>Here’s an example:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> num = [<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-keyword">let</span> tripled = num.map(<span class="hljs-function">(<span class="hljs-params">n</span>) =&gt;</span>  n * <span class="hljs-number">3</span>);
<span class="hljs-built_in">console</span>.log(tripled)  <span class="hljs-comment">// [ 3, 6, 9, 12 ]</span>
</code></pre>
<p>In each cycle, the program multiplies the num by three and adds it to an array.</p>
<p>We aren’t providing step-by-step instructions, yet the actions are carried out. This declarative approach is what makes React run so efficiently, and it's one of its standout features.</p>
<h2 id="heading-how-much-javascript-do-you-need-to-know-before-learning-react">How Much JavaScript Do You Need to Know Before Learning React?</h2>
<p>The initial question often is, how well should you know JavaScript before tackling React? I think it's important to have a solid understanding of JavaScript's core concepts before diving into any framework or library, as it will benefit you in the long run.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/TheAnkurTyagi/status/1422558697302249480?s=20"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>Let’s say you want to become a web developer and you want to use React as your primary technology. </p>
<p>Here is a high-level roadmap you can use to make sure you have the knowledge you need:</p>
<ul>
<li>Learn the basics of <a target="_blank" href="https://theankurtyagi.com/a-simple-and-effective-way-to-learn-practice-javascript/">JavaScript</a>. </li>
<li>Learn about web pages and web development technologies like <a target="_blank" href="https://www.freecodecamp.org/news/learn-html-and-css-from-the-ceo-of-scrimba/">HTML and CSS</a>.</li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/free-8-hour-node-express-course/">Learn the basics of Node.js and Express</a>.</li>
<li>Be able to write some kind of simple Node.js web app.</li>
<li>Learn about the different frameworks (React) and technologies developers use to develop Node.js apps.</li>
<li>Learn some kind of <a target="_blank" href="https://www.freecodecamp.org/news/full-stack-project-create-a-recipe-app-using-react-node-js/">database to use with Node.js</a>. </li>
<li>Learn computer science basics, like <a target="_blank" href="https://www.freecodecamp.org/news/learn-data-structures-and-algorithms/">Algorithms and Data structures</a>. </li>
<li>Learn <a target="_blank" href="https://www.freecodecamp.org/news/how-to-write-clean-code/">best practices for writing good code</a>.</li>
<li>Learn how to <a target="_blank" href="https://www.freecodecamp.org/news/an-introduction-to-software-architecture-patterns/">design the architecture</a> of a React.js app. </li>
</ul>
<p>Having some kind of plan in place is important. You can always change and adapt the plan, but if you don’t have the plan to begin with, you’ll be aimlessly wandering and you may end up frustrated and be more likely to give up. </p>
<p>So the simple answer to this question is, once you're confident in your JavaScript basics, go ahead and start learning about and building projects with React. You can <a target="_blank" href="https://www.freecodecamp.org/news/p/e2f91d79-f9c6-40a8-a53f-e61601faaeca/You%20can%20read%20more%20about%20the%20core%20JS%20concepts%20you%20should%20know%20in%20this%20guide.">read this article</a> to make sure you understand those fundamental JS concepts.</p>
<p>The tech field moves quickly with new languages or frameworks coming out often. If you focus on building a strong foundation, you'll be set to dive in.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/TheAnkurTyagi/status/1637061346708844545"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>Here are some more useful resources for learning JavaScript:</p>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/learn/">freeCodeCamp's JS course</a></li>
<li><a target="_blank" href="https://javascript.info/">The Modern JavaScript Tutorial</a></li>
<li><a target="_blank" href="https://github.com/leonardomso/33-js-concepts">'33 JavaScript Concepts' on GitHub</a></li>
<li><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript">Official JS Documentation (MDN)</a></li>
</ul>
<h2 id="heading-why-learn-react">Why Learn React?</h2>
<p>There are a number of reasons why learning this popular JavaScript library is beneficial. </p>
<p>Here are a few:</p>
<ul>
<li>React is well-liked in frontend development due to its reusable UI components, flexibility, and ease of debugging. This helps developers build large web apps more efficiently.</li>
<li>Chances are, you'll come across a React project either in your current job or in the future.</li>
<li>For a JavaScript programmer, facing React-based questions in job interviews is common.</li>
<li>Many frameworks or libraries, like NextJs and Gatsby, are built on top of React.</li>
<li>React enhances your development skills as it encourages good practices. It helps you consider data flow and global state management in your apps, along with its design patterns prompting you to think about various scenarios and edge cases.</li>
</ul>
<p>For more insights, you can read my article on <a target="_blank" href="https://theankurtyagi.com/why-you-should-learn-reactjs/">Why You Should Learn React</a>.</p>
<h3 id="heading-how-react-stands-out-from-other-javascript-ecosystem-tools">How React Stands Out from Other JavaScript Ecosystem Tools</h3>
<p>Firstly, React is speedy as it utilizes the virtual DOM to display data, updating only the altered parts when data changes.</p>
<p>React also encourages breaking down code into small, reusable chunks. This makes it manageable rather than tackling a large codebase all at once.</p>
<p>Beyond this, React simplifies debugging and speeds up large-scale development.</p>
<p>It's also SEO-friendly, which is crucial for business visibility on search engines like Google. And being open-source with a large community, React offers ample support and a variety of tools and extensions to ease its usage and debugging.</p>
<p>Now, with a better understanding of React's benefits, let’s go ahead and set up React in our local development environment.</p>
<h2 id="heading-how-to-set-up-react">How to Set Up React</h2>
<p>First, you need to install Node.js. It allows you to execute JavaScript code and run React applications on your computer.</p>
<p>You can install it from the <a target="_blank" href="https://nodejs.org/en">website</a> and check its current version using the code snippet below.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/image-99.png" alt="Image" width="600" height="400" loading="lazy">
<em>Setting up local development environment</em></p>
<p>Ensure you have Node 14.0.0 or higher on your machine to install the latest React version. </p>
<p>Then, to quickly set up a React app, there are a couple ways you can go. If you want, you can use the official tool, create-react-app. Executing <code>npx create-react-app my-app</code> will download the latest React version. But this method is no longer recommended for installing React. </p>
<p>Instead, you can create a React app using <a target="_blank" href="https://vitejs.dev/">Vite</a>, which is a development environment for modern applications.</p>
<p>Run the code snippet below within your terminal, provide a name for your project, and select React as the framework.</p>
<pre><code class="lang-bash">npm install
</code></pre>
<p>Start the development server by running the <code>npm run dev</code> within your terminal.</p>
<p>The app will launch on your local port <code>5173</code> by default.</p>
<p>Now, we're set to deep dive into the core concepts of React.</p>
<h2 id="heading-key-concepts-to-understand-in-react">Key Concepts to Understand in React</h2>
<h3 id="heading-react-components">React Components</h3>
<p>The browser returns a set of React elements, called components, that appear on the computer screen.</p>
<p>In React, a web page can be divided into multiple components that make up the entire elements on the screen. This helps us adhere to the DRY (Don’t Repeat Yourself) principle and write cleaner code.</p>
<p>React splits the UI into independent and reusable pieces (components). Each component name must start with a capital letter, which makes it readable. They have their own styles, APIs, logic, and structures. </p>
<p>There are two types of React Components:</p>
<ul>
<li>Class or stateful components </li>
<li>Functional or stateless components</li>
</ul>
<h4 id="heading-class-components-stateful">Class Components (Stateful)</h4>
<p>This method for creating React components is considered outdated. Although still supported by React, it is not recommended because new features in React are based on function components and hooks.</p>
<p>Class Components are declared using the JavaScript class keyword. They are called <code>stateful</code> because the variables within the components are linked to them using the JavaScript <code>this keyword</code>.</p>
<p>To create class components, you need to extend the default React component which contains a constructor function with all the required states. The class component displays its elements via a render function.</p>
<p>Let’s consider a Hello World component using Class Components.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> React, { Component } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HelloWorld</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Component</span> </span>{
  <span class="hljs-keyword">constructor</span>(props) {
    <span class="hljs-built_in">super</span>(props);
    <span class="hljs-comment">// Initialize state</span>
    <span class="hljs-built_in">this</span>.state = {
      <span class="hljs-attr">greeting</span>: <span class="hljs-string">'Hello, World!'</span>
    };
  }

  render() {
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>{this.state.greeting}<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>
    );
  }
}
</code></pre>
<p>The code snippet above creates a Class Component with a state called greeting containing a "Hello World" value.  The state is declared using the this.state keyword and the render method then displays the value of the greeting state within an h1 element.</p>
<p>Remember: you should generally avoid using class components in your applications, and use functional components instead – which we'll discuss now.</p>
<h4 id="heading-functional-components-stateless">Functional Components (Stateless)</h4>
<p>Functional components are a modern way of writing React. They follow the JavaScript ES6 method of writing functions. A functional component accepts a single argument known as props (object data), mostly returned with a JSX element.</p>
<p>Functional components receive data as props from different components. You'll learn how to manage state a little later in this guide.</p>
<p>Consider the example below:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> <span class="hljs-string">"useState"</span> <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [name, addName] = useState(<span class="hljs-string">''</span>);
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleAddName</span>(<span class="hljs-params">event</span>) </span>{
    addName(event.target.value);
  }
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>
        Name:
        <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">value</span>=<span class="hljs-string">{name}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleAddName}</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">form</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        Welcome {name}, I hope you will learn a lot in this Handbook. 
      <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>
  );
}
</code></pre>
<p>Function components are declared similarly to JavaScript functions. The difference is that components accept props and render JSX elements. You'll learn more about JSX next.</p>
<h2 id="heading-what-is-jsx">What is JSX?</h2>
<p>Every component you see uses JSX. The best thing about React is that you can easily embed JavaScript into JSX. It gives you the flexibility to build fast UIs. But wait, what is JSX?</p>
<p><a target="_blank" href="https://legacy.reactjs.org/docs/introducing-jsx.html">JSX</a> stands for JavaScript Syntax Extension, and it lets you use HTML-like syntax in your React components.</p>
<p>A function called <code>React.createElement()</code> enables us to create JSX elements in React. It accepts three arguments – the HTML element, an object containing the HTML element’s attribute, and the content of the HTML element.</p>
<p>Let’s see some examples: The code snippet above creates a button that displays “Click here” with a background colour and text color attributes.</p>
<pre><code class="lang-javascript">React.createElement( 
<span class="hljs-string">'button'</span>, 
{ <span class="hljs-attr">color</span> : <span class="hljs-string">'white'</span>, <span class="hljs-attr">backgroundColor</span>: <span class="hljs-string">'blue'</span>},
<span class="hljs-string">'Click here'</span>
)
</code></pre>
<p>But developers don’t create JSX elements this way. In fact, you don’t need to use the <code>React.createElement()</code> function because it creates a lot of unnecessary lines of code and will be difficult to create deeply nested JSX elements.</p>
<p>Instead, React provides an easier way of writing JSX elements. They are similar to HTML elements and accept an attribute called <code>className</code> that replaces the class attribute in HTML.</p>
<p>There are two rules concerning JSX elements:</p>
<ul>
<li><code>class</code> is a JavaScript keyword. Therefore, use <code>className</code> instead of class when styling your JSX elements.</li>
<li>A component can only return one JSX element. But when you need to display more than one JSX element, put them in a single container element.</li>
</ul>
<p>Let’s see some examples:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Greeting</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"heading"</span>&gt;</span>Welcome to React <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span></span>
  );
}
</code></pre>
<p>The code snippet above shows a Greeting component that returns a single JSX element. It displays “Welcome to React” using the h1 element. The className attribute adds the “heading” style to the JSX element.</p>
<p>Consider another component that returns multiple JSX elements:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Greeting</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"heading"</span>&gt;</span>Welcome to React <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
 <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>This message is sponsored by FreeCodeCamp
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  );
}</span>
</code></pre>
<p>The code snippet above returns more than one JSX element nested within a parent element. So it is important to note that all JSX elements must be within a parent container.</p>
<h3 id="heading-whats-the-difference-between-html-and-jsx">What’s the Difference Between HTML and JSX?</h3>
<p>JSX syntax looks like HTML. In HTML, we use class attributes for styling purposes. In JavaScript, ‘Class’ is a reserved word. So we can’t use the class keyword. For this, React uses <code>className</code> instead of Class as the default for styling.</p>
<p>Consider the example below:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
            <span class="hljs-selector-class">.heading</span> {
                <span class="hljs-attribute">color</span>: <span class="hljs-number">#3498db</span>;
            }

            <span class="hljs-selector-class">.paragraph</span> {
                <span class="hljs-attribute">font-size</span>: <span class="hljs-number">18px</span>;
            }
        </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"heading"</span>&gt;</span>Welcome to React<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"paragraph"</span>&gt;</span>Happy learning<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>The code snippet above shows an HTML document that displays a heading and paragraph elements on the web page. The elements are styled using the CSS class selector (HTML class attribute).</p>
<p>Let’s recreate this with JSX elements:</p>
<pre><code class="lang-javascript"><span class="hljs-string">'const App = () =&gt; {
    return (
        &lt;div&gt;
            &lt;h1 className='</span>heading<span class="hljs-string">'&gt;Welcome to React&lt;/h1&gt;
            &lt;p className='</span>paragraph<span class="hljs-string">'&gt;Happy learning&lt;/p&gt;
        &lt;/div&gt;
    );
};</span>
</code></pre>
<p>The code snippet above displays the heading and paragraph JSX elements and uses the className attribute to style each of them. You can add a CSS file containing your styles within your project.</p>
<h2 id="heading-what-is-react-state">What is React State?</h2>
<p>State is a key concept in React apps. You can think of state as a collection of information or objects that tell you where you can access and store your data. Anything outside of a component, for example, can’t access its data. It’s private and controlled by the component.</p>
<p>Every React component has its own state. Whenever a state changes, the component gets <strong>re-rendered.</strong> This happens when a user clicks a button, responds, or interacts with something on the page. </p>
<p>The children components can access these states via props (which we’ll discuss below) passed by the parent’s component.</p>
<p>Let’s consider some examples of React state:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [name, setName] = useState(<span class="hljs-string">""</span>)
<span class="hljs-keyword">const</span> [age, setAge] = useState(<span class="hljs-number">0</span>)
<span class="hljs-keyword">const</span> [products, setProducts] = useState([<span class="hljs-string">"rice"</span>, <span class="hljs-string">"beans"</span>])
</code></pre>
<p>The code snippet above shows some examples of React states. A state can be an array, string, object, or number. The useState hook accepts the initial value of the state as a parameter, and the state name and state function (for modifying its value) are destructured from the useState hook.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [stateName, stateFunction]= useState(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">initial_value</span>&gt;</span>)</span>
</code></pre>
<p>Consider a form that accepts a user’s name and age and displays them, like this:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserInfo</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [name, setName] = useState(<span class="hljs-string">''</span>);
  <span class="hljs-keyword">const</span> [age, setAge] = useState(<span class="hljs-number">0</span>);

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleNameChange</span>(<span class="hljs-params">event</span>) </span>{
    setName(event.target.value);
  }
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleAgeChange</span>(<span class="hljs-params">event</span>) </span>{
    setAge(<span class="hljs-built_in">parseInt</span>(event.target.value));
  }
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">label</span>&gt;</span>
        Name:
        <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">value</span>=<span class="hljs-string">{name}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleNameChange}</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">label</span>&gt;</span>
        Age:
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"number"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{age}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleAgeChange}</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">form</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
        Your name is {name} and your age is {age}.
      <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>
  );
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> UserInfo;
</code></pre>
<p>The code snippet above accepts the user’s name and age and stores them within the React states. The functions <code>handleNameChange</code> and <code>handleAgeChange</code> accept the user’s input and update the state’s values.</p>
<h3 id="heading-global-state-vs-local-state-in-react">Global State vs Local State in React</h3>
<p>Global States refers to state values used in multiple components within the application. In some cases, they can be updated anywhere within the application.</p>
<p>For instance, when a user changes from dark to light mode while using your application. This state affects the overall view of the application to ensure that all the components change colour.</p>
<p>To do this, you may need to declare a state like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [darkmode, setDarkMode] = useState(<span class="hljs-literal">false</span>)
</code></pre>
<p>This state gets updated when a user toggles a switch within the application. This state is global because it is passed into all the components within the application to ensure that their views change depending on the state's value.</p>
<p>Let's see an example:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [darkMode, setDarkMode] = useState(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Home</span> <span class="hljs-attr">darkMode</span>=<span class="hljs-string">{darkMode}</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Profile</span> <span class="hljs-attr">darkMode</span>=<span class="hljs-string">{darkMode}</span> <span class="hljs-attr">setDarkMode</span>=<span class="hljs-string">{setDarkMode}</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    );
};</span>
</code></pre>
<p>From the code snippet above, we have an App and a Profile component. The App component is a higher-order component that renders two components and contains a state called <code>darkMode</code> that toggles the mood of the application.</p>
<p>The sub-components (Home and Profile) accept the states and update their element's colour depending on the state's value.</p>
<p>From the Profile component, darkMode is a global state, and <code>name</code> is a local state because the name state is declared and bound to the Profile component only, and darkMode is available to other states apart from the App component.</p>
<p><strong>Local states</strong> are state variables bound to a component. They are used outside that component, meaning they are local to that component.</p>
<p>For instance, the name state is local to the Profile component:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> Profile = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [name, setName] = useState(<span class="hljs-string">"Ankur"</span>);
    <span class="hljs-keyword">const</span> toggleName = <span class="hljs-function">() =&gt;</span> setName(<span class="hljs-string">"Tyagi"</span>);
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hi, I'm {name}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{toggleName}</span>&gt;</span> Toggle Name <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>
    );
};
</code></pre>
<p>The code snippet above toggles the name state between “Ankur” and “Tyagi” when a user clicks the button, and the state is not used anywhere outside the Profile component.</p>
<h2 id="heading-props-in-react">Props in React</h2>
<p>Props are data transferred from a parent to a child component. Props can’t be modified, because they are read-only. A child component can not change the values of the props received from the parent component.</p>
<p>The diagram below shows that a component accepts props from another component and displays them as JSX elements within the component.</p>
<p><img src="https://lh7-us.googleusercontent.com/5r8Vl6fru32HhsepLC-r1liB6d1czoCjJSavSB_6eennnn3R8btkofqNSyytEEopXOLDpLBcN986CqV3GOyAtnw-LZuH5jHuavMUCF5-E9YDhrX0O1lkah1dtka2WzJppeurGjcGs8dllIZ8zE1Mjwk" alt="Image" width="345" height="146" loading="lazy">
<em>Diagram showing how props work</em></p>
<p>For example, let’s consider an application that allows users to toggle the application theme.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [darkMode, setDarkMode] = useState(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Home</span> <span class="hljs-attr">darkMode</span>=<span class="hljs-string">{darkMode}</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Profile</span> <span class="hljs-attr">darkMode</span>=<span class="hljs-string">{darkMode}</span> <span class="hljs-attr">setDarkMode</span>=<span class="hljs-string">{setDarkMode}</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    );
};</span>
</code></pre>
<p>The code snippet above shows that the Home and Profile components accept the darkMode state and its function as props. The Home component accepts the darkMode value and the Profile component accepts the state’s value and its function.</p>
<p>Now, how do we access the prop values within these components? You can do this via a method called Destructuring.</p>
<h3 id="heading-destructuring-props-in-react">Destructuring props in React</h3>
<p>Props destructuring is a simple phenomenon similar to how we get object values in JavaScript. Let’s see an example:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> profile = {
 <span class="hljs-attr">name</span>: <span class="hljs-string">"Ankur Tyagi"</span>,
 <span class="hljs-attr">age</span>: <span class="hljs-number">22</span>,
 <span class="hljs-attr">role</span>: <span class="hljs-string">"Technical Writer"</span>,
};
</code></pre>
<p>From the code snippet above, the object contains a name, age, and role property. In JavaScript, you can get the values of each property using the objectName.</p>
<p>Therefore, you can access each value as shown below:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">console</span>.log(profile.name)  <span class="hljs-comment">//"Ankur Tyagi"</span>
<span class="hljs-built_in">console</span>.log(profile.age) <span class="hljs-comment">//22</span>
<span class="hljs-built_in">console</span>.log(profile.role) <span class="hljs-comment">//"Technical Writer"</span>
</code></pre>
<p>However, destructuring provides a better and much cleaner way of getting the values within an object or array.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> profile = {
 <span class="hljs-attr">name</span>: <span class="hljs-string">"Ankur Tyagi"</span>,
 <span class="hljs-attr">age</span>: <span class="hljs-number">22</span>,
 <span class="hljs-attr">role</span>: <span class="hljs-string">"Technical Writer"</span>,
};
<span class="hljs-comment">//Destructuring the object</span>
<span class="hljs-keyword">const</span> { name, age, role } = profile;
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">`My name is <span class="hljs-subst">${name}</span>. I am a <span class="hljs-subst">${role}</span> and <span class="hljs-subst">${age}</span>years old`</span>);

<span class="hljs-keyword">const</span> friends = [<span class="hljs-string">"Tejas"</span>, <span class="hljs-string">"Brad"</span>, <span class="hljs-string">"Ankit"</span>]
<span class="hljs-comment">//Destructuring the array</span>
<span class="hljs-keyword">const</span> [first, second, third] = friends

<span class="hljs-built_in">console</span>.log(first)
</code></pre>
<p>From the code snippet above, the object properties and array values are destructured from the parent enabling us to reference each values using the property name or a variable name. Destructuring provides a cleaner way of accessing the values in an object or an array.</p>
<p>But React uses this ES6 syntax to simplify how you access values within objects and arrays and values from props. Note that when you pass data into components via props, the data is added as a property to the props object.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> [darkMode, setDarkMode] = useState(<span class="hljs-literal">false</span>);
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Home</span> <span class="hljs-attr">darkMode</span>=<span class="hljs-string">{darkMode}</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Profile</span> <span class="hljs-attr">darkMode</span>=<span class="hljs-string">{darkMode}</span> <span class="hljs-attr">setDarkMode</span>=<span class="hljs-string">{setDarkMode}</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    );
};</span>
</code></pre>
<p>The Profile component destructures the darkMode and setDarkMode values from the props object parameter, thereby, enabling us to interact with the data directly.</p>
<p>Every React component has a default props object that enables data sharing between components and you can access the props value using any of the destructuring methods below:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> Component = <span class="hljs-function">(<span class="hljs-params">props</span>) =&gt;</span> {
</code></pre>
<h3 id="heading-state-vs-props">State vs Props</h3>
<p>State and props are used for holding data within a React application. But they serve different purposes.</p>
<ul>
<li>States are mutable, and Props are immutable. Data stored in a state can be modified, while Props can not be modified (read-only).</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">//👇🏻 state</span>
    <span class="hljs-keyword">const</span> [name, setName] = useState(<span class="hljs-string">"Ankur"</span>);
    <span class="hljs-comment">////👇🏻 modify state</span>
    <span class="hljs-keyword">const</span> changeName = <span class="hljs-function">() =&gt;</span> setName(<span class="hljs-string">"Tyagi"</span>);

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Profile</span> <span class="hljs-attr">name</span>=<span class="hljs-string">{name}</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
};

<span class="hljs-comment">//👇🏻 accepts name as props</span>
<span class="hljs-keyword">const</span> Profile = <span class="hljs-function">(<span class="hljs-params">{ name }</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hi, I'm {name}<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Welcome to my tutorial<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>
    );
};
</code></pre>
<p>The code snippet above shows that when you declare a state, React allows you to create a function that modifies the state value. Also, states become props (read-only) when you pass them into other components.</p>
<ul>
<li>State are local to a component while Props are from a parent component. From the code snippet above, the name state is local to the App component but becomes a prop when passed into the Profile component.</li>
</ul>
<h2 id="heading-how-to-display-list-items-in-react">How to Display List Items in React</h2>
<p>So far, you’ve seen how to display states and props values within JSX elements in React. But in some cases, you may need to display list items on a webpage – for example, data received from an API endpoint. How do we render this in React? You’ll learn about that shortly.</p>
<p>Let’s consider the following code snippet that renders a list of foods:</p>
<pre><code class="lang-javascript"><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">App</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> products = [<span class="hljs-string">"Rice"</span>, <span class="hljs-string">"Beans"</span>, <span class="hljs-string">"Yam"</span>, <span class="hljs-string">"Eggs"</span>]
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
                {products.map((item) =&gt; (
                    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>{item}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            ))}
            <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>  
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    )
}
</code></pre>
<p>In React, the JavaScript function <a target="_blank" href="https://docs.google.com/document/d/1fGaC-08J_Seh0s7X5NAMX_lz5WiPed2QMifd91QsJqA/edit#heading=h.bvln7ojdubhk">array.map</a>() is used to render list items. But the code snippet is incomplete. Each list item must have a unique key prop to enable React keep track of each item within the list.</p>
<p><img src="https://lh7-us.googleusercontent.com/fzpTXQsNDQV3_vxZ_wKEKQepkzbnvXBMqhvjvZN2i9sRi2wCTct0Ao0KnjxbRs_Jz_rOz6ttXuDDxNprT7q89Tiw08OzWx3EckLPS3g9yIOXXv60QIjegMiwFUUu3gmquFUgQPkeuGrnhvh2y947nJ4" alt="Image" width="1366" height="151" loading="lazy">
<em>Warning stating that each child in the list should have a unique key prop.</em></p>
<p>To fix this, update the code snippet as shown below:</p>
<pre><code class="lang-javascript"><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">App</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> products = [<span class="hljs-string">"Rice"</span>, <span class="hljs-string">"Beans"</span>, <span class="hljs-string">"Yam"</span>, <span class="hljs-string">"Eggs"</span>]
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
                {products.map((item, index) =&gt; (
                    <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>&gt;</span>{item}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            ))}
            <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>

        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    )
}
</code></pre>
<p>The <a target="_blank" href="https://docs.google.com/document/d/1fGaC-08J_Seh0s7X5NAMX_lz5WiPed2QMifd91QsJqA/edit#heading=h.bvln7ojdubhk">array.map</a>() function accepts two arguments: the list item and its position within the array. So you can set the key prop to the index (item position) to enable React to distinguish each item from the others.</p>
<p>The key prop optimizes React's performance and is useful for performing CRUD operations with list items.</p>
<p>Let's consider a to-do list that enables users to create and delete to-do items.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</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">App</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [todoList, setTodoList] = useState([]);
    <span class="hljs-keyword">const</span> [todo, setTodo] = useState(<span class="hljs-string">""</span>);

    <span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
        e.preventDefault();
        setTodoList([...todoList, { todo, <span class="hljs-attr">id</span>: <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">1000</span> }]);
        setTodo(<span class="hljs-string">""</span>);
    };
    <span class="hljs-keyword">const</span> handleDelete = <span class="hljs-function">(<span class="hljs-params">id</span>) =&gt;</span> {
        <span class="hljs-keyword">const</span> newTodoList = todoList.filter(<span class="hljs-function">(<span class="hljs-params">item</span>) =&gt;</span> item.id !== id);
        setTodoList(newTodoList);
    };

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Todo List<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleSubmit}</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                    <span class="hljs-attr">type</span>=<span class="hljs-string">'text'</span>
                    <span class="hljs-attr">name</span>=<span class="hljs-string">'todo'</span>
                    <span class="hljs-attr">id</span>=<span class="hljs-string">'todo'</span>
                    <span class="hljs-attr">value</span>=<span class="hljs-string">{todo}</span>
                    <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setTodo(e.target.value)}
                    required
                /&gt;
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'submit'</span>&gt;</span>Add Todo<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>

            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                {todoList.map((item) =&gt; (
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{item.id}</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{item.todo}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> handleDelete(item.id)}&gt;Delete<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                ))}
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}
</code></pre>
<p>The <code>handleSubmit</code> function accepts the user’s input and adds it to the to-do list, and the <code>handleDelete</code> function removes the selected to-do from the to-do list using its id.</p>
<p>The code snippet above displays the items within the to-do list and uses the items’ ids to identify each to-do when adding and removing items from the list.  </p>
<p><img src="https://lh7-us.googleusercontent.com/vRUhBY660BL-aIScb_jK2opXIJZq-xh-KJsl91-g-1TIR9CqH1Z-iuxgR0oRHSO_D900oWk7yYgR-eulo8iTRJJBaClsAdwzyf4XH5AHWT1TXClRqaBjps7TecBpDr9Y5j-l2mEri7POJYaGhNoufmE" alt="Image" width="600" height="332" loading="lazy">
<em>Example todo list - adding and deleting items</em></p>
<h2 id="heading-react-event-handlers">React Event Handlers</h2>
<p>When we click some button or interact with the UI, we expect a response. This is made possible with Event handlers. These event handlers determine which action is performed when the user interacts with them (that is, when some event is performed).</p>
<p>If you know how to handle events in JavaScript, it won't be that hard to understand how to do it in React. There are just a couple differences.</p>
<p>In React, event handlers are written in camelCase form like this: onClick, onChange, and so on.</p>
<p>Here’s an example that shows the difference between writing event handlers in HTML and React. Both code snippet executes a function called handleClick when a user clicks the button.</p>
<p>In HTML:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"handleClick()"</span>&gt;</span>
You clicked a button
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>In React:</p>
<pre><code class="lang-javascript">&lt;button onClick={handleClick}&gt;
You clicked a button
&lt;/button&gt;
</code></pre>
<p>In React, you need to use event handlers when submitting a form and modifying the value of a state. Let’s see some examples:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</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">App</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [todo, setTodo] = useState(<span class="hljs-string">""</span>);

    <span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
        e.preventDefault();
        <span class="hljs-built_in">console</span>.log({ todo });
    };

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Todo List<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{handleSubmit}</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                    <span class="hljs-attr">type</span>=<span class="hljs-string">'text'</span>
                    <span class="hljs-attr">name</span>=<span class="hljs-string">'todo'</span>
                    <span class="hljs-attr">id</span>=<span class="hljs-string">'todo'</span>
                    <span class="hljs-attr">value</span>=<span class="hljs-string">{todo}</span>
                    <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setTodo(e.target.value)}
                    required
                /&gt;
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">'submit'</span>&gt;</span>Add Todo<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}
</code></pre>
<p>There are two event handlers in the code snippet above: the <code>onSubmit</code> and the <code>onChange</code> handlers.</p>
<p>The <code>onChange</code> event handler updates the todo state with the form’s input as the user enters a value into the form field.</p>
<p>The <code>onSubmit</code> event handles the form submission. In most React forms, you’ll come across the function <code>event.preventDefault()</code>. It prevents the page from reloading (default action) every time a user submits the form.</p>
<h2 id="heading-react-hooks">React Hooks</h2>
<p>Hooks are a critical feature of React that were introduced in V16.8.</p>
<p>In class components, we encounter some problems:</p>
<ul>
<li>We have to take care of Auto-binding and “This”</li>
<li>The code gets lengthier, and serval lifecycle methods are hard to follow.</li>
<li>It is hard to share logic and reuse components</li>
</ul>
<p>To solve all of these class-related problems, the React team created hooks. They help you write cleaner, more readable, and maintainable code. So let’s learn about few of the Hooks and how to use them.</p>
<h3 id="heading-the-usestate-hook">The useState hook</h3>
<p>useState is used for creating a state variables. It enables us to create and change state variables within our components.</p>
<p>useState returns a pair of values. The first one is the current state and the second is the function that updates it.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [value, setValue] = useState(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">initial</span> <span class="hljs-attr">Value</span>&gt;</span>)</span>
</code></pre>
<p>Let’s create a counter using the useState hook:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Counter</span> = (<span class="hljs-params"></span>) =&gt; </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);
</code></pre>
<p>We initialize a count state and set it to “0”. The handleClick function modifies the count state using the setCount function to increase the count value by 1.</p>
<p>The code snippet below modifies the count state when the user clicks the increment and the decrement buttons.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Counter</span> = (<span class="hljs-params"></span>) =&gt; </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClickInc</span>(<span class="hljs-params"></span>)</span>{
        setCount(<span class="hljs-function"><span class="hljs-params">counter</span> =&gt;</span> counter + <span class="hljs-number">1</span>)
    }
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleClickDec</span>(<span class="hljs-params"></span>)</span>{
</code></pre>
<h3 id="heading-the-useeffect-hook">The useEffect hook</h3>
<p>The useEffect hook is an essential hook that enables us to perform actions when various changes occur within a React component, such as data fetching, when a component re-renders, and so on.</p>
<p>The useEffect hook accepts two parameters: a function and a dependency array. The function is executed depending on the conditions within the dependency array.</p>
<pre><code class="lang-javascript">useEffect(<span class="hljs-function">()=&gt;</span> {
</code></pre>
<p>There are three ways you can use the useEffect hook:</p>
<ul>
<li>Without a dependency array</li>
<li>With an empty dependency array</li>
<li>With a dependency array containing values.</li>
</ul>
<h4 id="heading-without-a-dependency-array">Without a dependency array</h4>
<p>A useEffect hook without a dependency array will run every time the component re-renders due to an event or a state change. You should not use the useEffect hook this way because it leads to performance issues.</p>
<p>Assuming there is a useEffect hook within the Counter component, it will return when the component mounts on the page and every time the counter changes.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</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">Counter</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-keyword">const</span> increment = <span class="hljs-function">() =&gt;</span> setCount( <span class="hljs-function"><span class="hljs-params">prev</span> =&gt;</span> prev + <span class="hljs-number">1</span>)
    <span class="hljs-keyword">const</span> decrement = <span class="hljs-function">() =&gt;</span> setCount( <span class="hljs-function"><span class="hljs-params">prev</span> =&gt;</span> prev - <span class="hljs-number">1</span>)

    useEffect(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Run everytime count changes"</span>);
    });


    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Count: {count}<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">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{increment}</span>&gt;</span>Increase<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">onClick</span>=<span class="hljs-string">{decrement}</span>&gt;</span>Decrease<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}
</code></pre>
<p>Note that you should avoid using useEffect without its dependency array – it will cause performance issues within your application.</p>
<h4 id="heading-with-an-empty-dependency-array">With an empty dependency array</h4>
<p>A useEffect hook can also contain an empty dependency array. This means that the function within the useEffect function should run only once – when the component mounts or is loaded on the web page.</p>
<p>This method is mostly used when you need to fetch data from an API endpoint and display it on the web page when it loads.</p>
<p>Here is an example, the useEffect function runs only once (when the page mounts). An empty dependency array tells React that the useEffect runs once – when the component is rendered on the web page.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</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">App</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-keyword">const</span> increment = <span class="hljs-function">() =&gt;</span> setCount( <span class="hljs-function"><span class="hljs-params">prev</span> =&gt;</span> prev + <span class="hljs-number">1</span>)
    <span class="hljs-keyword">const</span> decrement = <span class="hljs-function">() =&gt;</span> setCount( <span class="hljs-function"><span class="hljs-params">prev</span> =&gt;</span> prev - <span class="hljs-number">1</span>)

    useEffect(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Run only once when component is mounted"</span>);
    }, []);

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Count: {count}<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">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{increment}</span>&gt;</span>Increase<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">onClick</span>=<span class="hljs-string">{decrement}</span>&gt;</span>Decrease<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}
</code></pre>
<h4 id="heading-with-a-dependency-array-containing-values">With a dependency array containing values</h4>
<p>In this previous section, you learned that declaring a useEffect function without a dependency array causes performance issues. So how can we execute function that runs when the counter changes?</p>
<p>This is where the dependency array helps. It contains the values that the function depends on. The function runs when the component is rendered and when the value of variables within the array changes.</p>
<p>Let’s update the useEffect hook to run only when the count changes.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</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">App</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>);

    <span class="hljs-keyword">const</span> increment = <span class="hljs-function">() =&gt;</span> setCount( <span class="hljs-function"><span class="hljs-params">prev</span> =&gt;</span> prev + <span class="hljs-number">1</span>)
    <span class="hljs-keyword">const</span> decrement = <span class="hljs-function">() =&gt;</span> setCount( <span class="hljs-function"><span class="hljs-params">prev</span> =&gt;</span> prev - <span class="hljs-number">1</span>)

    useEffect(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"useEffect runs when count changes"</span>);
    }, [count]);

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Count: {count}<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">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{increment}</span>&gt;</span>Increase<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">onClick</span>=<span class="hljs-string">{decrement}</span>&gt;</span>Decrease<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}
</code></pre>
<h3 id="heading-the-usereducer-hook">The useReducer hook</h3>
<p>The useReducer is commonly used in components that have a large number of states and multiple event handlers. It enables you to manage complex states within your application.</p>
<p>You should just use the useState hook when there are a few states within your components. Use the useReducer hook when you have a lot of states to manage.</p>
<p>The useReducer hook is divided into four parts: the state, the reducer function, the action, and the dispatch function.</p>
<p>The state is an object containing all states declared within the application.</p>
<p>The reducer function manipulates the state directly and returns a copy of the result, and the dispatch function triggers the reducer function when an event occurs.</p>
<p>The action is an object containing a type and a payload property.</p>
<p>The type property specifies the exact action to be executed by the reducer function, and the payload can accept data from the user or other parts of the application.</p>
<p>Let’s see how this works by re-creating the Counter component using the useReducer hook:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useReducer } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
</code></pre>
<p>The useReducer hook accepts two arguments: the reducer function and the state object. It returns the states and the dispatch function.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [state, dispatch] = useReducer(reducerFunction, {states});
</code></pre>
<p>From the Counter component, the useReducer hook accepts the reducer function and the counter’s initial state.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {useReducer} <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>  
<span class="hljs-comment">//👇🏻 declares the useReducer hook</span>
 <span class="hljs-keyword">const</span> [state, dispatch] = useReducer(reducer, { <span class="hljs-attr">counter</span>: <span class="hljs-number">0</span> }));
</code></pre>
<p>After declaring the useReducer hook, you need to create the reducer function which handles the state manipulation.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//👇🏻 reducer function</span>
 <span class="hljs-keyword">const</span> reducer = <span class="hljs-function">(<span class="hljs-params">state, action</span>) =&gt;</span> {
  <span class="hljs-keyword">switch</span> (action.type) {
   <span class="hljs-keyword">case</span> <span class="hljs-string">"increase"</span>:
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">counter</span>: state.counter + <span class="hljs-number">1</span> };
   <span class="hljs-keyword">case</span> <span class="hljs-string">"decrease"</span>:
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">counter</span>: state.counter - <span class="hljs-number">1</span> };
   <span class="hljs-keyword">default</span>:
    <span class="hljs-keyword">return</span> state;
  }
 };
</code></pre>
<p>The reducer function accepts a state and action parameter. The action object determines the action to be carried out by the reducer function.</p>
<p>Finally, we have the dispatch function that states the type of action and passes its value into the reducer function to enable it carry out the required task.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> increaseCounter = <span class="hljs-function">() =&gt;</span> {
  dispatch({ <span class="hljs-attr">type</span>: <span class="hljs-string">"increase"</span> });
 };

 <span class="hljs-keyword">const</span> decreaseCounter = <span class="hljs-function">() =&gt;</span> {
  dispatch({ <span class="hljs-attr">type</span>: <span class="hljs-string">"decrease"</span> });
 };
</code></pre>
<p>You can also pass data into the reducer function via the dispatch. For example, you can increase the counter by 2 by passing the data as a payload via the dispatch function.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> increaseBy2 = <span class="hljs-function">() =&gt;</span> {
  dispatch({<span class="hljs-attr">type</span>: <span class="hljs-string">"increaseBy2"</span>, <span class="hljs-attr">payload</span>: {<span class="hljs-attr">number</span>: <span class="hljs-number">2</span>}})
 }
</code></pre>
<p>Then, create its action within the reducer function as shown below:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> reducer = <span class="hljs-function">(<span class="hljs-params">state, action</span>) =&gt;</span> {
  <span class="hljs-keyword">switch</span> (action.type) {
   <span class="hljs-keyword">case</span> <span class="hljs-string">"increase"</span>:
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">counter</span>: state.counter + <span class="hljs-number">1</span> };
   <span class="hljs-keyword">case</span> <span class="hljs-string">"decrease"</span>:
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">counter</span>: state.counter - <span class="hljs-number">1</span> };
   <span class="hljs-keyword">case</span> <span class="hljs-string">"increaseBy2"</span>:
    <span class="hljs-keyword">return</span> { <span class="hljs-attr">counter</span>: state.counter + action.payload.number };
   <span class="hljs-keyword">default</span>:
    <span class="hljs-keyword">return</span> state;
  }
 };
</code></pre>
<p>The useReducer hook is much more useful in components containing numerous states and various state modifications, because it enables you to write the code in a cleaner way.</p>
<h2 id="heading-data-flow-in-react">Data Flow in React</h2>
<p>Data flows from top to bottom in React, meaning you can only pass data from a parent to a child component. This is where props come in handy. But sometimes you may need to pass data from a child to a parent component. How do we achieve this?</p>
<p>You can accomplish this by lifting the state up. This involves moving the state from the child component to the parent component where the state is needed. Keep in mind, though, that this solution may not be effective because it might lead to prop drilling.</p>
<h3 id="heading-what-is-prop-drilling">What is Prop Drilling?</h3>
<p><strong>Prop drilling</strong> occurs when the parent and child components are not directly linked, and you need to pass the data via multiple components before it reaches the child component that needs the data.</p>
<p>Let's consider an example: suppose we have an App component that renders various parts of the application. Then, a Products component displays the list of available products, and we need to show the total number of products within the Nav component at the top of the screen.</p>
<p>To solve this, you may need to move the products into the App component and pass them as props into all the child components until they reach the Nav and Product components. This process is called prop drilling. The product data will have to pass through components that do not require it.</p>
<p><img src="https://lh7-us.googleusercontent.com/kdujx9yA0oVGx6DiXxszXCu6FzSOdWxJfxhmPWjQl4DrHKySGQzKh497-rS1w9AnaR8Apyc339xz4ekZGHI-ot3s4_EckbSI1bimkmdJ47zbWgV7R2CNIs0zkj4hc88_ZHftQ_riO4Ae4GtVWTvw5b4" alt="Image" width="800" height="400" loading="lazy">
<em>Diagram illustrating how prop drilling works</em></p>
<p>State management libraries like Redux Toolkit, React Context API, and Zustand can help solve this problem. They enable you to create a store within your application that allows all the components to access the store independently without interacting with a parent component.</p>
<p>Any changes made to a state within the store reflect within all the required components. Feel free to research and learn about this topic more.</p>
<p><img src="https://lh7-us.googleusercontent.com/jT2wKxV-0aNiqh7KPc8kWkcrvFQAVF_FQr1jt07BU_ZKquHvHA-95rNB9xmQ135x40GkkfkXqbkwa40-GVI0q3H8KtayuTNqXRcnKfaNeZRMsxFI0i9S30-8tBs0uGkOqvzigDzWb0cKoXCzokTM2Y8" alt="Image" width="800" height="400" loading="lazy">
<em>Diagram showing how using a state management library can help</em></p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>That’s all for this tutorial. I hope you found the discussion enlightening and are now better equipped to make informed decisions in your React journey.</p>
<p>If you're eager to dive deeper into React and expand your skills, I've got a treasure trove of resources right on my blog. From beginner-friendly tutorials to advanced tips and tricks, there's something for everyone looking to master this powerful library.</p>
<p>Don't miss out on my latest React tutorials:</p>
<ul>
<li><strong>Understanding React Hooks: A Beginner's Guide</strong> - <a target="_blank" href="https://theankurtyagi.com/react-hooks/">Read Here</a></li>
<li><strong>State Management in React: Redux vs Context API</strong> - <a target="_blank" href="https://theankurtyagi.com/react-state-management-a-complete-in-depth-look-at-hooks-context-api-and-redux/">Read Here</a></li>
<li><strong>How to Use and Validate Forms in React</strong> - <a target="_blank" href="https://theankurtyagi.com/how-to-validate-forms-in-react/">Read Here</a></li>
</ul>
<p>If you want to learn React according to industry standards, here is where I’ve shared my experience: <a target="_blank" href="https://dev.to/tyaga001/7-best-practices-for-keeping-a-react-project-clean-and-efficient-1ee3">Best Practices for Keeping a React Project Clean and Efficient.</a></p>
<p>You can get in touch if you have any questions or corrections. I’m expecting them.</p>
<p>And if you found this tutorial useful, please share it with your friends and colleagues who might benefit from it as well. Your support enables me to continue producing useful content for the tech community.</p>
<p>Now it’s time to take the next step by subscribing to my <a target="_blank" href="https://theankurtyagi.substack.com/"><strong>newsletter</strong></a> and following me on <a target="_blank" href="https://twitter.com/theankurtyagi"><strong>Twitter</strong></a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/12/image-168.png" alt="Image" width="600" height="400" loading="lazy">
<em>A newsletter on Career, Business, Writing and Life advice for Engineers</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Technical Writing for Developers – Why You Should Have a Blog and How to Start One ]]>
                </title>
                <description>
                    <![CDATA[ Having a blog is a useful asset for developers. It can be a lead magnet, as well as a place where you can sell digital products you create.  It can also simply be the place where you build a loyal audience within your niche, share your knowledge and ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/technical-writing-for-developers/</link>
                <guid isPermaLink="false">66c3755a86ad38fd9838388f</guid>
                
                    <category>
                        <![CDATA[ Blogging ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technical writing ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ankur Tyagi ]]>
                </dc:creator>
                <pubDate>Mon, 23 Oct 2023 17:22:04 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/10/Orange---Yellow-Gradient-Make-Design-Blog-Banner--42-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Having a blog is a useful asset for developers. It can be a lead magnet, as well as a place where you can sell digital products you create. </p>
<p>It can also simply be the place where you build a loyal audience within your niche, share your knowledge and ideas, and help other devs.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1697186764980/c8f8f03f-0f7d-4fa7-8fc6-9dd45b0a124b.png" alt="Blog- Ankur Tyagi" width="1920" height="1080" loading="lazy">
<em>The benefits of having a personal blog as a developer</em></p>
<p>In this article, I'll share why I blog, as well as why I think it's a great idea for you, too.</p>
<h2 id="heading-why-i-write-technical-articles">Why I Write Technical Articles</h2>
<p>Here's something I've learned over the past five years of blogging: don't start by telling the audience why you wrote a blog post. Instead, tell them why they should read it, and then do your best to prove yourself right.</p>
<p>I recently revamped my blog and began writing consistently, to the point where I now write a new tutorial once a week. And I’ve already seen a lot of good results.</p>
<p>I've also started paying more attention to my analytics. This helps me give people what they want, answer their questions, and keep them coming back for more. I'll talk a bit more about analytics later.</p>
<p>I found my mission in teaching developers about topics like how to start your own blog, how tools such as React, Angular, JavaScript, and TypeScript work, and so on. I release a new article every week on my blog <a target="_blank" href="https://theankurtyagi.com/blog/"><strong>theankurtyagi.com</strong></a><strong>,</strong> explaining all I know about a specific topic. </p>
<p>If you are into React or developing on the Web, you won't want to miss it. And I just started creating a new React writing boot camp where I share my 14 years of tech experience and how I've built multiple applications.</p>
<p>Now I'll share my thoughts on writing technical tutorials, and why I think you should have your own blog as a developer.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ul>
<li><a class="post-section-overview" href="#heading-understand-your-tech-abilities-and-soft-skills">Understand Your Tech Abilities and Soft Skills</a></li>
<li><a class="post-section-overview" href="#heading-doubts-you-may-have-before-starting-a-developer-blog">Doubts You May Have Before Starting a Developer Blog</a></li>
<li><a class="post-section-overview" href="#heading-how-to-start-writing-your-own-blog">How to Start Writing Your Own Blog</a></li>
<li><a class="post-section-overview" href="#heading-why-writing-is-helpful-for-developers">Why Writing is Helpful for Developers</a></li>
<li><a class="post-section-overview" href="#heading-how-writing-can-help-advance-your-software-engineering-career">How Writing Can Help Advance Your Software Engineering Career</a></li>
<li><a class="post-section-overview" href="#heading-how-to-write-an-effective-technical-blog">How to Write an Effective Technical Blog</a></li>
</ul>
<h2 id="heading-understand-your-tech-abilities-and-soft-skills">Understand Your Tech Abilities and Soft Skills</h2>
<p>If you’re working in tech, you can take advantage of the skills you already have and share your knowledge with the community. Do any of the following statements apply to you?</p>
<ul>
<li>You excelled at much of your past work, and you know that companies (or people) will pay for that work.</li>
<li>People ask you for advice based on your past work.</li>
<li>Somebody said, “You should write something or record a video to document your experiences because you offered me so much value when you mentored me or helped me in a peer programming session or saved me from that production bug while reviewing my PR pull request”</li>
</ul>
<p>The value you bring to your audience comes from a combination of your perspective and your interpersonal skills.</p>
<p>Think back to what other people have asked you to do, or what you've volunteered to do in your career—because you're good at those activities. </p>
<p>But don’t stop with technical skills. While core technical skills are useful, they are not all you'll need for long-term success as you move up in your tech career. That’s because the more responsibility you take for your client’s growth and results, the more the more helpful you can be to other people’s work. And this often starts with communication skills – so don't neglect them.</p>
<h2 id="heading-doubts-you-may-have-before-starting-a-developer-blog">Doubts You May Have Before Starting a Developer Blog</h2>
<h3 id="heading-i-am-not-an-expert-writer">I am not an expert writer</h3>
<p>Every developer has a unique angle and a perspective on something worth sharing.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/TheAnkurTyagi/status/1607633576714977280"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>You may believe you don't know as much as senior developer X, but junior developer Y may have much less experience and would benefit from reading about your career journey and learnings. </p>
<p>Also, the best time to teach something is right after you've learned it because you remember how it feels when you didn't know it – so you can more effectively teach a beginner about it.</p>
<p>In this case, you can write for a student who has recently learned something new. Every day, I learn something new.</p>
<p>Approximately 80% of what I end up writing these days is something I discovered while researching a topic on the internet.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/TheAnkurTyagi/status/1698668505292300461"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h3 id="heading-im-just-a-good-enough-writer">I’m just a good enough writer</h3>
<p>I'm not a professional writer, but that doesn't bother me. Remember that you will never become a better writer unless you write every day for years. If you practice enough, and actively try to improve your writing, you'll eventually improve your writing skills.</p>
<p>To create something good, just start writing. To create something exceptional, write, revise, and revise again. The key to producing excellent writing lies in dedicating significant time and effort to the rewriting process.</p>
<h3 id="heading-all-developers-fear-criticism">All Developers Fear Criticism</h3>
<p>Some things are fragile and break with a sudden shock, like a water glass.</p>
<p>Some things are not fragile and remain intact even when faced with a sudden impact or shock, like a teddy bear.</p>
<p>And some things, like our immune system, are anti-fragile, meaning they improve when subjected to a shock. Your immune system does not develop properly if you are not exposed to a series of smaller shocks as a child.</p>
<p>Similarly, to become a better developer in tech as a result of criticism, you must develop an anti-fragile attitude towards criticism. That is, with every bit of critique you receive, you can learn to use it to become better.</p>
<p>If you do not learn this, you will remain at your current level. You can do this at the meta-level as well: become anti-fragile in dealing with criticism in general, and become a better human being as a result of it.</p>
<p>Empowering self-hacking starts with cultivating a heightened awareness of your own emotional state. Recognizing anger, for instance, weakens its hold on you. When angry, you may engage in actions you otherwise wouldn't. (Anger can sometimes serve as a signal that our boundaries have been crossed.)</p>
<p>Certain online platforms, like Reddit and Hacker News, are known for their unfiltered feedback. Embrace this as an opportunity for growth. Remember, you only truly evolve when faced with challenges. </p>
<p>You also have the choice not to engage if you prefer. Concerned about potential negative comments on a controversial post? You can always disable comments altogether.</p>
<p>Also, remember that not everyone gives valuable criticism in tech.</p>
<ul>
<li>Some people just don’t connect with your work.</li>
<li>Some people won’t like what you’re writing.</li>
<li>Some people aren't yet ready to critique your work.</li>
<li>Some people are just mean.</li>
<li>Some people like your style of writing. Others won't.</li>
</ul>
<p>And sometimes when people stare at screens all day, like those of us in tech, they can unwittingly be more harsh and direct. It's not a great way to communicate, but it might not be malicious.</p>
<p>If you can get past the sting of the critiques and use them to improve your writing, you'll have a lot to offer the developer community through your tutorials.</p>
<h2 id="heading-how-to-start-writing-your-own-blog">How to Start Writing Your Own Blog</h2>
<p>Rich people might seem to have it all  – money, power, influence, and whatever else they crave. But there’s one thing they want that money can’t buy: good ideas.</p>
<p>Writing is how you share your ideas, and if you do it well, you can often gain access to a coffee or dinner with almost any tech CEO in software engineering.</p>
<p>I've been writing for around 5 years, so here's my take:</p>
<ul>
<li><strong>Just start writing</strong>: If you've been wanting to start your own blog, stop thinking about it, and just do it. Open a blog on WordPress and start writing. That's my first advice to anyone. Don't waste time on the technicalities, just write. You can move your blog to another platform later.</li>
<li><strong>Be consistent</strong>: This is hard. I fail to follow this rule most of the time. But a successful blog is a blog that's been going on for ages, and where the creator posts regularly. I've posted 100+ posts since 2020 on my blog and 100+ for various startup developer tools – and that's why it has been working well for me.</li>
<li><strong>Don't try to be perfect</strong>: You are not writing a book. A blog is to share knowledge quickly or to share what's on your mind. No hesitation, once you have something, publish it. People might call you out for saying something wrong, fine – that's free publicity and you'll have learned something new.</li>
<li><strong>Mix short posts with long posts</strong>: It's difficult to keep writing high-quality content and long blog posts all the time. So write small ones from time to time to fill in between more in-depth guides. This is what makes people keep going to your blog. It's like a Twitter feed, it needs to have something every time people check it.</li>
<li><strong>Write on current/popular subjects</strong>: What did you learn recently that could be helpful to other people? What did you have trouble learning because there was no good resource on the subject? You could be that resource. What is Google Trends saying? Are there any trendy topics in your field? What are people talking about recently?</li>
</ul>
<p>I generally start with an idea instead of a title. Then I write down what I know about it. Next, I put those thoughts in an order that I can use to tell a story. Finally, I research to confirm that what I think I know is indeed true and correct. That is where it becomes interesting.</p>
<p>Sometimes I'm completely wrong, and the story becomes about that. The truth is often more interesting than the assumptions. </p>
<p>Sometimes I discover things about what I knew that are more interesting than the original idea. Sometimes what I thought I knew turns out to be true and I just back it up with facts and the idea alone was interesting enough to begin with. </p>
<p>And lastly, sometimes the idea is actually not worth sharing, and I shelve it until the time feels right or I find a new approach to the subject.</p>
<h2 id="heading-why-writing-is-helpful-for-developers">Why Writing is Helpful for Developers</h2>
<h3 id="heading-you-learn-much-faster">You learn much faster</h3>
<p>Writing is a great skill for a developer for many reasons, including:</p>
<ul>
<li>You learn by doing.</li>
<li>You learn faster.</li>
<li>You build a lot of connections which can generate future leads and opportunities.</li>
<li>You can work on different tech stacks</li>
<li>It’s a great form of self-expression.</li>
<li>It encourages creativity.</li>
<li>It helps to connect with your imagination.</li>
</ul>
<p>I enjoy writing because it forces me to read more. And as a result, I learn a lot and stay sharp while working and collaborating with others. </p>
<p>One of the best ways for me to learn is by doing. I pick a topic about which I believe I know something and drill down through things I didn't know or hadn't even considered.</p>
<p>My best tip for anyone willing to start a blog: Read, read, read, write, write, write, read, read, write, write, write, write, read, read, read, read, write, write, write, edit, edit, edit, edit, edit, edit, edit, read, read, read, write again.</p>
<p>They say you never fully understand a topic until you can explain it.</p>
<p><strong>And blogging is often low-hanging fruit.</strong></p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/TheAnkurTyagi/status/1596975332791578624"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>Every developer should make a habit of blogging about their learnings in software engineering. Writing helps you discover if you understand a topic as well as you think you do. </p>
<p>Teaching and writing are great ways to learn, and they're good habits to start because you already have firsthand experience. Just commit to writing 12 blog posts a year, see what happens, and plan your next steps based on that. </p>
<blockquote>
<p><em>As David Perell says, “Read to collect the dots, write to connect them.”</em></p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1697184338558/b8bd0707-6b86-4b75-9f04-736dc1ad853d.png" alt="Why Writing is a Low Hanging Fruit" width="1918" height="1048" loading="lazy">
<em>How writing is a low hanging fruit for experienced developers</em></p>
<h3 id="heading-writing-involves-critical-thinking">Writing involves critical thinking</h3>
<p>So many things never fully connected for me until I wrote about them. And so many other things I thought I understood well in my head turned out to be half-baked when I start writing about them.</p>
<p>My blog "<a target="_blank" href="https://theankurtyagi.com/a-software-developers-guide-to-writing/">A Software Developer's Guide to Writing</a>" is a complete guide on how to start a blog.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/GergelyOrosz/status/1604959976870629385"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>Writing is a form of self-expression that can be therapeutic, even if you never intend to publish your work.</p>
<h2 id="heading-how-writing-can-help-advance-your-software-engineering-career">How Writing Can Help Advance Your Software Engineering Career</h2>
<p>I’ve elevated my career in software engineering thanks to my writing.</p>
<p>It was 2017, and I'd begun writing about small things I was learning while building a few web applications for fun as a side project. I'd also gotten the opportunity to contribute to internal developer docs in my previous job as a tech lead.</p>
<p>That old blog has since died — I stopped writing on it a long time ago, and the content, which is now completely obsolete, has vanished. But I doubt I could have imagined starting my current small writing business without it.</p>
<p>Through my current blog, I have a lot of connections and opportunities for freelance gigs and part-time roles. It's also helped me in my consulting career.</p>
<h3 id="heading-writing-tutorials-can-help-you-learn-a-new-tech-stack">Writing tutorials can help you learn a new tech stack</h3>
<p>Writing helps in learning a new technology stack this is something I've done a few times with my writing. If I am interested in one type of technology stack and then become interested in another, I will write several posts about it.</p>
<p>For example, last summer I wrote about multiple developer tools in this article: <a target="_blank" href="https://theankurtyagi.com/how-i-use-medusa-to-create-a-powerful-next-js-e-commerce-store/"><strong>How to Create a Powerful Next.js E-commerce Store</strong></a><strong>.</strong></p>
<p><img src="https://theankurtyagi.com/wp-content/uploads/2023/04/Clean-Work-Place-Blog-Banner-34-2048x1152-1-1024x576.png" alt="medusa-nextJs-e-commerce-store" width="1024" height="576" loading="lazy">
<em>How to build an e-commerce store with Medusa a blog by Ankur Tyagi</em></p>
<h3 id="heading-other-benefits-of-technical-writing">Other benefits of technical writing</h3>
<p>Writing about technical topics is a great way for you to show what you know and what you're able to do. It can also help build your authority in tech by establishing your reputation and building your personal brand.</p>
<p>It can also improve your opportunities for networking and career advancement, as more people read your tutorials. And the more you write, the better your communication and collaboration skills will become.</p>
<p>Another benefit I discovered from sharing my writing publicly is that it caught the attention of many company founders, who then offered me writing jobs. They found me through my blog posts online, even though I hadn’t applied to work with them. Take a look at the below inbound cold email I received. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1697188149814/1087eecc-fc19-410a-9b76-f6f7a79a4e4d.png" alt="cold email " width="2060" height="814" loading="lazy">
<em>An inbound cold email from a tech founder</em></p>
<p>So having a blog is an effective way to demonstrate that you are passionate, know your stuff, and can communicate effectively in writing. The simplest way to begin is to write posts about topics that have interested you or problems you've solved.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/TheAnkurTyagi/status/1698932253613580739"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h2 id="heading-how-to-write-an-effective-technical-blog">How to Write an Effective Technical Blog</h2>
<h3 id="heading-keep-your-topics-consistent">Keep your topics consistent</h3>
<p>I only follow blogs that have a clear focus or theme.</p>
<p>In my case, I talk about Web Development, React, Developer Tools, and so on, and I subscribe and consistently read other developers' blogs that are focused on a particular area of tech.</p>
<p>Don't just rant about anything that comes to mind. Use a professional tone and cultivate your own unique writing style.</p>
<h3 id="heading-setup-a-routine">Setup a routine</h3>
<p>If you aim to write a blog post weekly, make sure you follow through. Writing two posts a week is even more ideal. I write every day because I know that if I skip one day, I will skip another, and so on until I stop posting altogether.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/TheAnkurTyagi/status/1701843420342268367"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h3 id="heading-write-blog-posts-in-advance">Write blog posts in advance</h3>
<p>Don't post the same day you write. Instead, schedule your content for a week later and review it the day before.</p>
<p>This is a big deal. By waiting a week before you post and looking at it again, the quality of your content will improve. A lot!</p>
<p>Write it a week or more in advance. Even if you take a few days off or are sick, you are less likely to miss a blog post day. It's also a good idea to make your posts public ahead of time. <a target="_blank" href="https://theankurtyagi.com/">WordPress</a> makes it very simple, and it is also possible to schedule a specific time and day to write on a consistent basis.</p>
<p>If you write down 5 ideas per day, that will amount to 1,825 ideas in a year (assuming it's not a leap year). Among these ideas, you're likely to find a few outstanding topics for your blog posts. Try to maintain a roster of potential blog post ideas. When inspiration strikes, you'll find the perfect topic for that day in your backlog.</p>
<p>In your articles, take things a step further and make your content 10x better by telling your audience the "why" of things. This is much harder to do.</p>
<p>If your writing is poor quality or inconsistent, you'll end up with the wrong audience. Write with intention and purpose. Write to become better. Instead of writing more, start editing more.</p>
<h3 id="heading-build-your-writing-not-your-website">Build your writing, not your website</h3>
<p>You can use big blogging platforms to increase your visibility. Think long-term. In the next ten years, every blog post you write could be worth thousands of views.</p>
<p>When a developer comes to me and says, "Hi Ankur, I'm a developer and want to start a blog. What should I do?", here are the two options I discuss with them:</p>
<h4 id="heading-option-1-start-publishing-your-articles-on-an-established-blogging-platform">Option 1: start publishing your articles on an established blogging platform</h4>
<p>Option one is starting with a platform like <a target="_blank" href="https://theankurtyagi.com/top-5-best-blogging-platforms-for-software-engineers-in-2023/">Medium, Hashnode, DevTo, HackerNoon, or Dzone</a>.</p>
<p>You can start writing articles and publishing them on one of thoes platforms, and work on building your credibility as a writer. Set a goal for yourself – for example, try to get to 100K+ post views/year.</p>
<p>Then you can start learning about and implementing SEO strategies, especially if you start writing on your own blog.</p>
<p><strong>One potential challenge</strong> with this approach is that you may not technically own the content you publish on other's sites (check their rules). The company could even go out of business or shut down their service, and you may lose all your articles. </p>
<p>So here's my advice on how to handle this: if you stick with writing for a year, then it's time to start moving slowly onto your own website and posting your articles there.</p>
<p><strong>Another challenge</strong> is that you don't necessarily want to spend all your time building a personal blog and just have one post titled "How I made this beautiful blog with X framework" to show for it. Instead, you could've had dozens of blog posts on rented spaces (like Medium, Hashnode, DevTo). Build your brand before building a house.</p>
<p>You can treat Medium, Hashnode, Twitter, and other platforms as traffic-acquisition channels rather than a home for your long-form content. Because no moderator can delete your blog posts from your own website.</p>
<h4 id="heading-option-2-create-your-own-blog-site-from-scratch">Option 2: create your own blog site from scratch</h4>
<p>Option two involves starting with your own blog website from the beginning. You can either build your own or use something like Ghost/WordPress/ any CMS of your choice. Then you can syndicate your articles to existing blogging platforms until enough people learn about your own blog. After that, you can stop syndicating content if you want.</p>
<p>But I'd advise you to write 2-3 blog posts every month for a year before you even think about building your own fancy, tech-savvy website and publishing there.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/TheAnkurTyagi/status/1662847806841683973"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>Treat every blog post as a mini side project and see how it feels. If, after a year, you find you're still enjoying writing, then continue doing it. If not, blogging may not be for you.</p>
<p><strong>One challenge</strong> with this second approach (building your own blog and starting there) is that many developers with full-time jobs find it hard to spare time and energy, even though they want to write. So I think it's better to start small – and maybe share your posts on an existing platform at first – to avoid this common pitfall. </p>
<p>Again, it's better to focus on building your personal brand and your reputation as a writer first, before investing time in creating a dedicated website.</p>
<p>While it can be harder to reach a large audience on your own personal blog, you have to start somewhere. </p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/TheAnkurTyagi/status/1677250760575270912"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p><strong>Another challenge</strong> with this approach is the tendency to over-engineer a simple static blog website, and spending too much time perfecting that site instead of writing. </p>
<p>When I was starting a blog, I actually got caught up in it all – but one of my friends guided me through the process and showed me WordPress, which was a lifesaver. In the case of your blog, people are often interested more in your storytelling than your actual blog website.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/TheAnkurTyagi/status/1522462918062788611"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>In my opinion, in the end, being the <a target="_blank" href="https://www.hanselman.com/blog/your-words-are-wasted">owner of your own blog space &gt;&gt;&gt; always using rental space</a>. Just make sure you prioritize your tasks and focus along the way.</p>
<p>If you're curious about my blog post writing style, I invite you to check out this post: <a target="_blank" href="https://theankurtyagi.com/the-ultimate-guide-for-developers-to-write-blog-posts-for-success-in-2023/">How I Write My Blog Posts</a>.</p>
<h3 id="heading-building-an-audience">Building an audience</h3>
<p>This is related to the previous point. If you're just getting started, you might get 1 or 10 or 100 visitors per week if you share your posts (unless you go viral on a sharing platform like Reddit, Hacker News, or Twitter).</p>
<p>Search engines like <em>Google</em> can often provide consistent traffic (SEO), but this is a very long game to play. It's easy to get discouraged if you write but no one reads your blog posts.</p>
<p>The initial years of writing can be tough and boring. Despite very good writing, your blog will likely grow slowly. Big views may seem to be a distant dream. The effects of writing kick off only after a certain amount of time when your blog may start to build up momentum. Patience and persistence are key.</p>
<p>So, actively look for an audience. Write on your blog and republish your blog posts to Medium, Hashnode, DevTo, and Hackernoon (remember to add a canonical tag back to your site so that you don't annoy Google with duplicate content). Having an audience will boost your enthusiasm and determination.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/TheAnkurTyagi/status/1673935217021669377?s=20"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h3 id="heading-promote-your-content">Promote your content</h3>
<p>When you hit the “publish,” button you’ve only done 50% of the work.</p>
<ul>
<li>You had an idea</li>
<li>You’ve done writing along with researching the topic</li>
<li>You’ve looked for typos and fixed grammar errors</li>
<li>You made sure to have no plagiarism</li>
</ul>
<p>Now it's time to promote your writing.</p>
<p>Post on Twitter/LinkedIn if you have a following, otherwise try to share on HN, Reddit, and so on.</p>
<p>Here's what Arnold Schwarzenegger said about why marketing your product is equally as important as making the product:</p>
<blockquote>
<p>Whenever I finished filming a movie, I felt my job was only half done. Every film had to be nurtured in the marketplace. You can have the greatest movie in the world, but if you don’t get it out there, if people don’t know about it, you have nothing. It’s the same with poetry, with painting, with writing, with inventions.  </p>
<p>It always blew my mind that some of the greatest artists, from Michelangelo to van Gogh, never sold much because they didn’t know how. They had to rely on some schmuck (some agent or manager or gallery owner) to do it for them. Picasso would go into a restaurant and do a drawing or paint a plate for a meal. Now you go to these restaurants in Madrid, and the Picassos are hanging on the walls, worth millions of dollars.  </p>
<p>That wasn’t going to happen to my movies.  </p>
<p>Same with bodybuilding, same with politics—no matter what I did in life, I was aware that you had to sell it.</p>
</blockquote>
<p>As Ted Turner said, “Early to bed, early to rise, work like hell, and advertise.”</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/tferriss/status/1709560867568709719"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>In a similar way, whenever I finished writing a blog, I felt my job was half done.</p>
<p>I wanted every article to be distributed across multiple platforms. You can write the best blog in the world, but if you don't get it out there and people aren't aware of it, you're out of luck. It's the same with a book, a course, a community, a product, a startup, and your writing.</p>
<blockquote>
<p><strong>Marketing boils down to caring</strong>: Don’t confuse the word “marketing” with advertising, announcing, spamming, or giving away branded crap. Really, “marketing” just means being considerate. Marketing means making it easy for people to notice you, relate to you, remember you, and tell their friends about you.  –Derek Sivers | <a target="_blank" href="https://twitter.com/sivers">@sivers</a></p>
</blockquote>
<h3 id="heading-your-blog-is-your-media-platform">Your blog is your media platform</h3>
<p>Don’t just think of it as a blog. See it as a startup: the goal of a blog is to disseminate information that drives sales and future opportunities.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/peer_rich/status/1712777165098398127"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>Sales may make you uncomfortable, but think of it as selling ideas or your own expertise.</p>
<h3 id="heading-set-up-an-email-collecting-form-or-a-newsletter">Set up an email collecting form or a newsletter</h3>
<p>A big Social media following is a business card, but a big email list gives you longevity.</p>
<p>In reality, you don't have full ownership of your Twitter, LinkedIn, or Medium following. Your true ownership lies in your email list. I recommend <a target="_blank" href="https://theankurtyagi.substack.com">SubStack</a>; it's user-friendly, free, and exceptional.</p>
<h3 id="heading-a-blog-should-be-professional-not-personal">A blog should be professional, not personal</h3>
<p>Your audience isn't primarily interested in you. They visit your blog in anticipation of problem-solving (if they arrived from a Google search) or seeking valuable information to aid them in specific tasks. Write with their needs at the forefront to be truly helpful.</p>
<h3 id="heading-dont-use-ads-at-the-beginning">Don't use ads at the beginning</h3>
<p>Don’t put ads on your blog. I don't think it's worth it unless you have 10,000+ visitors a day.</p>
<h3 id="heading-watch-your-analytics-and-use-them-to-guide-you">Watch your analytics and use them to guide you</h3>
<p>Peter Drucker said, "You can't manage what you can't measure."</p>
<p>If building an audience was a piece of cake, everyone would do it. Here are my tips on how to use your analytics to improve your blog and the content you create:</p>
<p><strong>Find Out What People Like to Read:</strong> Look at the pages that get the most visits. What are they about? This is what your audience loves the most. Write more about those topics.</p>
<p><strong>Understand How People Find You on Internet:</strong> Examine the search terms that lead people to your site. This tells you what they're interested in and what questions they have. Answer those questions in your writing.</p>
<p><strong>Know What Keeps Them Reading:</strong> See which pages keep people on your site the longest. This is the content that really grabs them. Use that style or topic as a model for your other posts.</p>
<p><strong>Learn From the Bounces:</strong> On the flip side, see which pages have the highest bounce rate. These are the ones that might need a bit of tweaking. </p>
<p><em>Ask yourself, "Why are people leaving?</em>" and then make changes accordingly.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/10/image-97.png" alt="My blog's analytics" width="600" height="400" loading="lazy">
<em>My blog's analytics</em></p>
<h3 id="heading-read-books-blog-posts-twitter-reddit-and-hackernews">Read books, Blog posts, Twitter, Reddit, and HackerNews</h3>
<p>Keep yourself up to date on the topics you want to write about. I write about software engineering, and Twitter is a never-ending source of topic ideas. The same is true for books and blogs. freeCodeCamp is also fantastic for this.</p>
<p>Every day, I spend 1-2 hours scrolling Twitter for inspiration. It's not a waste of time. It's the catalyst my brain requires to see things I would never have seen in offline life. Luck is not as random as you think. Before a lottery ticket can win a jackpot, someone has to buy it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1697189511612/83db750b-f21a-4bf8-aeba-921db9c50ce5.jpeg" alt="Twitter- an idea generator " width="944" height="1600" loading="lazy">
<em>My daily screen time</em></p>
<h3 id="heading-wake-up-early-if-possible">Wake up early if possible</h3>
<p>Set your alarm clock, get up, and begin writing. I used to get up at 7 a.m. because I haven't had to commute since 2020 (I only work remotely), so I could take it easy. I now get up at 5 a.m. and have a new post scheduled for the following week by 7 a.m. </p>
<p>When you finish a task, such as writing a new blog post, by 7 a.m., you feel energised to tackle the rest of the day. If you can, get up even earlier (I can't, or I'll spend the rest of the day in a zombie state).</p>
<h3 id="heading-start-using-dnd-dont-disturb-mode">Start using DND (Don't Disturb) Mode</h3>
<p>It's all too easy to become distracted while researching for a blog post on the internet. I've turned off all notifications on my phone. Writing, like programming, is best done without interruptions so that the task at hand can occupy your entire consciousness.</p>
<p>My advice is to set aside dedicated time on your calendar, disable notifications, set your phone aside, and commit to your keyboard or typewriter, pen and paper, or whatever.</p>
<p>These are the only notifications which are "ON" on my mobile:</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/TheAnkurTyagi/status/1636793854140481562"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<p>### </p>
<h2 id="heading-thanks-for-reading"><strong>Thanks for Reading!</strong></h2>
<p>That's it for this tutorial. I hope you learned something new today.</p>
<p>If you did, please share so that it reaches others as well.</p>
<p>You can connect with me on <a target="_blank" href="https://twitter.com/TheAnkurTyagi">Twitter</a>.</p>
<h3 id="heading-want-to-read-more-interesting-blog-posts"><strong>Want to read more interesting blog posts?</strong></h3>
<p>You can read more tutorials like this one <a target="_blank" href="https://theankurtyagi.com/blog/">on my personal blog here</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
