<?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[ Yemi Ojedapo - 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[ Yemi Ojedapo - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 21 May 2026 16:10:23 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/Hyemiie/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Test and Improve AI Applications with an Evaluation Flywheel ]]>
                </title>
                <description>
                    <![CDATA[ In traditional programming, developers rely on unit tests to catch mistakes in applications. But when building AI products, that safety net doesn't exist. Responses can shift with model updates, data changes, and subtle fluctuations in prompts or ret... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-test-and-improve-ai-applications-with-an-evaluation-flywheel/</link>
                <guid isPermaLink="false">69491adc842069e2b48bbae7</guid>
                
                    <category>
                        <![CDATA[ ai agents ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ optimization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Yemi Ojedapo ]]>
                </dc:creator>
                <pubDate>Mon, 22 Dec 2025 10:18:04 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766082262126/bc54e004-7acc-49fc-b228-24524f250427.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In traditional programming, developers rely on unit tests to catch mistakes in applications. But when building AI products, that safety net doesn't exist. Responses can shift with model updates, data changes, and subtle fluctuations in prompts or retrieval results. The usual testing methods like unit tests with Pytest or Jest, integration tests, CI pipelines, fail to catch accuracy drops, hallucinations, or regressions, and these silent failures can become real production risks.</p>
<p>In this article, you’ll learn why traditional testing methods fall short for AI systems and how an evaluation flywheel can be used as a practical approach to testing and improving AI applications. The sections below break the evaluation flywheel down step by step, from identifying the problem to implementing a repeatable evaluation loop.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-why-does-traditional-testing-fail-for-ai-applications">Why Does Traditional Testing Fail for AI applications?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-the-evaluation-flywheel">What is the Evaluation Flywheel?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-drawing-parallels-to-familiar-practices">Drawing Parallels to Familiar Practices</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-silent-failures-matter-a-real-world-example">Why Silent Failures Matter: A Real-World Example</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-an-evaluation-flywheel">How to Create an Evaluation Flywheel</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-tools-and-frameworks-you-can-use-for-evaluation">Tools and Frameworks you can use for evaluation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-a-complete-evaluation-loop-looks-like-in-practice">What a Complete Evaluation Loop Looks Like in Practice</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-key-takeaways">Key Takeaways</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-why-does-traditional-testing-fail-for-ai-applications">Why Does Traditional Testing Fail for AI applications?</h2>
<p>In standard programming, tests assume deterministic behavior. This means the same input is expected to always produce the same output. For example:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">authenticate_user_age</span>(<span class="hljs-params">age: int</span>) -&gt; str:</span>
    limit = <span class="hljs-number">18</span>

    <span class="hljs-keyword">if</span> age &gt;= limit:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Access granted"</span>
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> <span class="hljs-string">"User doesn't meet the age limit"</span>

<span class="hljs-comment"># Test </span>
<span class="hljs-keyword">assert</span> authenticate_user_age(<span class="hljs-number">20</span>) == <span class="hljs-string">"Access granted"</span>
<span class="hljs-keyword">assert</span> authenticate_user_age(<span class="hljs-number">16</span>) == <span class="hljs-string">"User doesn't meet the age limit"</span>
</code></pre>
<p>The response from this function is always predictable. You can write tests once and trust they'll catch errors forever.</p>
<p>However, AI models don’t behave the same way every time, they generate output based on probabilities. A query like “best programming practices” may produce strong guidance one day, and outdated or incomplete advice the next. This shift can happen because of changes in the underlying model, updates to retrieval components, or gradual data drift. Without a structured evaluation process in place, these inconsistencies slip into production unnoticed and can quietly weaken the system’s performance.</p>
<h2 id="heading-what-is-the-evaluation-flywheel">What is the Evaluation Flywheel?</h2>
<p>The evaluation flywheel is a continuous improvement system where test cases representing real user behavior are passed through multiple evaluation steps to assess the output of AI models. The results don't just tell you whether the system passed or failed, they feed directly into the next cycle of improvement.</p>
<pre><code class="lang-plaintext">┌─────────────┐
│   Collect   │
│ Test Cases  │
└──────┬──────┘
       │
       ▼
┌─────────────┐
│     Run     │
│ Evaluations │
└──────┬──────┘
       │
       ▼
┌─────────────┐      ┌─────────────┐
│  Identify   │─────▶│   Improve   │
│  Failures   │      │   System    │
└─────────────┘      └──────┬──────┘
                            │
                            ▼
                       ┌─────────────┐
                       │   Repeat    │
                       └─────────────┘
</code></pre>
<p>Here's how it works in practice:</p>
<ul>
<li><p><strong>Collect test cases</strong> — Gather examples from real user interactions or create synthetic scenarios. These should reflect the kind of tasks and input your system needs to handle.</p>
</li>
<li><p><strong>Run evaluations</strong> — Pass each test case through a series of checks. The check can either be programmatic (automated metrics like relevance scores or hallucination detectors) or require manual review (like verifying legal advice accuracy or brand voice consistency).</p>
</li>
<li><p><strong>Identify failures</strong> — Detect where the model goes wrong, this can include hallucinations, irrelevant responses, or mistakes on corner-cases.</p>
</li>
<li><p><strong>Improve the system</strong> — Based on those failures, refine prompts, improve training or retrieval data, or adjust architectural components.</p>
</li>
<li><p><strong>Repeat the cycle</strong> — Re-run the updated system on the existing and newly collected cases. Over time, this grows and strengthens your evaluation suite and boosts system reliability.</p>
</li>
</ul>
<h2 id="heading-drawing-parallels-to-familiar-practices">Drawing Parallels to Familiar Practices</h2>
<p>If you've written software before, the evaluation flywheel will feel familiar. It mirrors patterns that are already used in engineering. For instance,</p>
<p><strong>Unit tests → Evaluation datasets</strong><br>Unit tests confirm a function returns the right output. Evaluation datasets play the same role for AI: they're ground-truth queries and answers that guard against regressions.</p>
<p><strong>Test-driven development (TDD) → Evaluation-driven development (EDD)</strong><br>In TDD, you write tests before code. In EDD, you write evaluation cases before shipping prompts or updating models. This replaces assumptions with verifiable results.</p>
<p><strong>CI/CD pipelines → Continuous evaluation pipelines</strong><br>CI/CD runs checks automatically on every code change. Continuous evaluation does the same for models: it runs automated quality checks every time you tweak a prompt, retrain, or swap out a component.</p>
<p>The key difference is subtle but important. Traditional software tests check whether a function returns the right value or type. AI evaluation tests check whether the system produces the right <em>meaning</em>. That's harder to measure, but the principle is the same: build a safety net that grows stronger with every cycle.</p>
<h2 id="heading-why-silent-failures-matter-a-real-world-example">Why Silent Failures Matter: A Real-World Example</h2>
<p>AI systems often behave differently in production than they do in development. A model that seems solid in testing can drift, hallucinate, or silently fail when facing real-world input.</p>
<p><strong>Case in point</strong>: A fraud detection model passed all monitoring metrics yet missed a spike in fraud. An ML engineer shared how their production monitoring dashboards tracked latency, throughput, and error rates, everything showed green. But fraudulent transactions were slipping through at twice the normal rate. Nobody noticed because existing observability tools focused on pipeline health, not prediction quality.</p>
<p>This silent failure cost the company significant losses. The system seemed fine by traditional metrics. It measured system performance—latency, throughput, uptime—but ignored what mattered most: prediction accuracy. As fraudsters adapted their tactics, the model drifted, and without proper evaluation loops, the degradation went undetected for weeks.</p>
<p>Source: <a target="_blank" href="https://insightfinder.com/blog/model-drift-ai-observability/">InsightFinder</a>.</p>
<h3 id="heading-why-this-example-matters">Why This Example Matters</h3>
<ul>
<li><p><strong>Silent failures aren't always bugs</strong> — They often stem from models failing to adapt to shifting patterns in the real world.</p>
</li>
<li><p><strong>Static evaluation isn't enough</strong> — You need continuous, real-world feedback loops to detect when assumptions no longer hold.</p>
</li>
<li><p><strong>Data drift has business impact</strong> — Model degradation isn't just technical, it translates directly into revenue loss, security breaches, or damaged user trust.</p>
</li>
</ul>
<h2 id="heading-how-to-create-an-evaluation-flywheel">How to Create an Evaluation Flywheel</h2>
<p>To show how to build a flywheel and how it works, let's create one for a customer support chatbot that answers questions about a SaaS product.</p>
<h3 id="heading-step-1-build-your-ai-system"><strong>Step 1: Build Your AI System</strong></h3>
<p>Create your initial product: prompts, retrieval logic, and integrations. For our chatbot:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">answer_support_question</span>(<span class="hljs-params">question: str</span>) -&gt; str:</span>
    <span class="hljs-comment"># Retrieve relevant docs from knowledge base</span>
    context = retrieve_docs(question, top_k=<span class="hljs-number">5</span>)

    <span class="hljs-comment"># Generate answer using LLM</span>
    prompt = <span class="hljs-string">f"""You are a helpful customer support agent.

Context: <span class="hljs-subst">{context}</span>

Question: <span class="hljs-subst">{question}</span>

Provide a clear, accurate answer based on the context."""</span>

    response = llm.generate(prompt)
    <span class="hljs-keyword">return</span> response
</code></pre>
<p><strong>How this works:</strong> This function defines the core chat logic, it takes a customer’s question and returns an AI-generated answer. First, it searches your knowledge base to find the five most relevant documents using <code>retrieve_docs()</code>. These documents provide context about your product or policies. Next, it constructs a prompt that includes this context and the user's question, then sends it to a language model. The LLM reads the context and generates a relevant answer, which the function returns.</p>
<h3 id="heading-step-2-identify-test-cases">Step 2: Identify Test Cases</h3>
<p>Build an evaluation set that reflects real user behavior. The more representative your test cases are, including common cases, edge cases, and ambiguous inputs, the better your model can catch failures before they reach production.</p>
<p><strong>Sources for test cases:</strong></p>
<ul>
<li><p>Previous customer support tickets</p>
</li>
<li><p>Common FAQ topics</p>
</li>
<li><p>Edge cases discovered in beta testing</p>
</li>
<li><p>Synthetic scenarios (hypothetical but realistic queries)</p>
</li>
</ul>
<p>Example test cases:</p>
<pre><code class="lang-python">test_cases = [
    {
        <span class="hljs-string">"question"</span>: <span class="hljs-string">"How do I reset my password?"</span>,
        <span class="hljs-string">"expected_elements"</span>: [<span class="hljs-string">"settings page"</span>, <span class="hljs-string">"reset link"</span>, <span class="hljs-string">"email"</span>],
        <span class="hljs-string">"category"</span>: <span class="hljs-string">"account_management"</span>
    },
    {
        <span class="hljs-string">"question"</span>: <span class="hljs-string">"What's your refund policy?"</span>,
        <span class="hljs-string">"expected_elements"</span>: [<span class="hljs-string">"30 days"</span>, <span class="hljs-string">"full refund"</span>, <span class="hljs-string">"contact support"</span>],
        <span class="hljs-string">"category"</span>: <span class="hljs-string">"billing"</span>
    },
    {
        <span class="hljs-string">"question"</span>: <span class="hljs-string">"Can I export my data to CSV?"</span>,
        <span class="hljs-string">"expected_elements"</span>: [<span class="hljs-string">"yes"</span>, <span class="hljs-string">"export button"</span>, <span class="hljs-string">"dashboard"</span>],
        <span class="hljs-string">"category"</span>: <span class="hljs-string">"features"</span>
    },
    {
        <span class="hljs-string">"question"</span>: <span class="hljs-string">"Does your API support webhooks?"</span>,
        <span class="hljs-string">"expected_elements"</span>: [<span class="hljs-string">"yes"</span>, <span class="hljs-string">"webhook endpoints"</span>, <span class="hljs-string">"documentation"</span>],
        <span class="hljs-string">"category"</span>: <span class="hljs-string">"technical"</span>
    }
]
</code></pre>
<p><strong>How this works:</strong> Here, we define a set of representative test cases to evaluate the AI system. Each test case includes the user’s question, a list of key elements expected in the answer, and a category for organization. These cases help ensure the chatbot is tested against real-world scenarios, edge cases, and important information that should appear in responses.</p>
<h3 id="heading-step-3-evaluate-outputs">Step 3: Evaluate Outputs</h3>
<p>Define evaluation criteria based on what matters for your use case: accuracy, faithfulness, safety, relevance, tone. Then measure the output against these criteria.</p>
<p>Evaluation happens in two main ways:</p>
<h4 id="heading-automated-evaluation">Automated Evaluation</h4>
<p>Use programmatic metrics and LLM-as-judge patterns:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">evaluate_response</span>(<span class="hljs-params">question: str, response: str, expected_elements: list</span>) -&gt; dict:</span>
    scores = {}

    <span class="hljs-comment"># 1. Faithfulness: Does response contain expected elements?</span>
    scores[<span class="hljs-string">'contains_key_info'</span>] = all(
        elem.lower() <span class="hljs-keyword">in</span> response.lower() 
        <span class="hljs-keyword">for</span> elem <span class="hljs-keyword">in</span> expected_elements
    )

    <span class="hljs-comment"># 2. Relevance: Semantic similarity to question</span>
    scores[<span class="hljs-string">'relevance'</span>] = calculate_semantic_similarity(question, response)

    <span class="hljs-comment"># 3. Safety: Check for problematic content</span>
    scores[<span class="hljs-string">'is_safe'</span>] = <span class="hljs-keyword">not</span> contains_harmful_content(response)

    <span class="hljs-comment"># 4. Tone: Use LLM-as-judge</span>
    judge_prompt = <span class="hljs-string">f"""Rate the helpfulness of this support response on a scale of 1-5.

Question: <span class="hljs-subst">{question}</span>
Response: <span class="hljs-subst">{response}</span>

Score (1-5):"""</span>

    scores[<span class="hljs-string">'helpfulness'</span>] = int(llm.generate(judge_prompt))

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

<span class="hljs-comment"># Run evaluation</span>
<span class="hljs-keyword">for</span> test_case <span class="hljs-keyword">in</span> test_cases:
    response = answer_support_question(test_case[<span class="hljs-string">'question'</span>])
    scores = evaluate_response(
        test_case[<span class="hljs-string">'question'</span>],
        response,
        test_case[<span class="hljs-string">'expected_elements'</span>]
    )
    test_case[<span class="hljs-string">'scores'</span>] = scores
    test_case[<span class="hljs-string">'response'</span>] = response
</code></pre>
<p><strong>How this works:</strong> The <code>evaluate_response()</code> function applies four different checks to each AI response:</p>
<ul>
<li><p>First, it verifies faithfulness by checking if all expected elements appear in the response using simple string matching.</p>
</li>
<li><p>Second, it calculates semantic similarity, a measure of how closely the responses meaning match the intent of the questions, using embeddings.</p>
</li>
<li><p>Third, it runs a safety check to flag any problematic content.</p>
</li>
<li><p>Fourth, it uses an LLM as a judge by asking a more powerful model (like GPT-4) to rate the helpfulness of the response on a 1-5 scale.</p>
</li>
</ul>
<p>The loop then runs the evaluation for every test case. It generates a response for each question, evaluates it using the <code>evaluate_response</code> function, and then stores both the scores and the response back in the test case. This creates a complete dataset of test results for analysis and further improvements.</p>
<p>Common Automated Metrics:</p>
<ul>
<li><p><strong>Semantic similarity (0.0–1.0):</strong> This is measured by converting the question and response into vector embeddings and calculating cosine similarity. The score shows how closely the response matches the intent of the question, even if the wording differs.</p>
</li>
<li><p><strong>ROUGE / BLEU scores:</strong> The model’s output is compared to reference answers by checking n-gram overlap. These metrics help spot regressions, though scores can be modest for open-ended answers.</p>
</li>
<li><p><strong>LLM-as-judge:</strong> A stronger model (like GPT-4 or Claude) can rate the response on a fixed scale, such as 1–5. These ratings give a sense of quality and are useful for tracking improvements or drops over time.</p>
</li>
<li><p><strong>Retrieval metrics (Precision@k, Recall@k):</strong> For retrieval-based systems, these metrics calculate how many relevant documents appear in the top-k results. Precision shows accuracy of the retrieved set, and recall indicates completeness.</p>
</li>
<li><p><strong>Custom validators:</strong> Simple rule-based checks, like regex patterns, keywords, or length limits, ensure responses meet hard requirements. These help catch issues automated metrics might miss.</p>
</li>
</ul>
<h4 id="heading-manual-evaluation">Manual Evaluation</h4>
<p>Automated metrics can't capture everything. Subjective qualities like tone, empathy, and brand voice require human judgment, as do small factual errors that slip past keyword checks and similarity scores.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Flag cases for human review</span>
needs_review = [
    case <span class="hljs-keyword">for</span> case <span class="hljs-keyword">in</span> test_cases 
    <span class="hljs-keyword">if</span> case[<span class="hljs-string">'scores'</span>][<span class="hljs-string">'helpfulness'</span>] &lt; <span class="hljs-number">3</span> 
    <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> case[<span class="hljs-string">'scores'</span>][<span class="hljs-string">'contains_key_info'</span>]
]

<span class="hljs-comment"># SMEs review and annotate</span>
<span class="hljs-keyword">for</span> case <span class="hljs-keyword">in</span> needs_review:
    annotation = get_sme_feedback(case)
    case[<span class="hljs-string">'human_rating'</span>] = annotation[<span class="hljs-string">'rating'</span>]
    case[<span class="hljs-string">'improvement_notes'</span>] = annotation[<span class="hljs-string">'notes'</span>]
</code></pre>
<p>This code filters test cases to find responses that need human attention, those scoring below 3 for helpfulness or missing important information. Subject matter experts review these flagged cases and provide ratings with helpful feedback. Their input helps you spot patterns that automated metrics miss and shows you where to improve your prompts, retrieval setup, or system settings.</p>
<p><strong>When to use manual evaluation:</strong></p>
<ul>
<li><p>Assessing tone, empathy, or brand voice</p>
</li>
<li><p>Detecting subtle hallucinations automated checks miss</p>
</li>
<li><p>Validating edge cases with domain-specific nuance</p>
</li>
<li><p>Creating ground truth labels for training evaluation models</p>
</li>
</ul>
<h3 id="heading-step-4-learn-and-improve">Step 4: Learn and Improve</h3>
<p>Once you've identified failures, adjust the controllable parts of your AI system (the "configs"):</p>
<p><strong>Common configuration levers:</strong></p>
<ul>
<li><p><strong>Prompts</strong> — Add instructions, examples, constraints</p>
</li>
<li><p><strong>Retrieval</strong> — Change chunk size, top-k, reranking strategy</p>
</li>
<li><p><strong>Model</strong> — Switch models, adjust temperature, max tokens</p>
</li>
<li><p><strong>Context</strong> — Modify system instructions, add memory</p>
</li>
<li><p><strong>Post-processing</strong> — Add validation, formatting, safety filters</p>
</li>
</ul>
<p><strong>Example improvement cycle:</strong></p>
<pre><code class="lang-python"><span class="hljs-comment"># Problem discovered: Chatbot missing key details</span>
failing_case = {
    <span class="hljs-string">"question"</span>: <span class="hljs-string">"What's your refund policy?"</span>,
    <span class="hljs-string">"response"</span>: <span class="hljs-string">"We offer refunds in certain cases."</span>,
    <span class="hljs-string">"issue"</span>: <span class="hljs-string">"Too vague, missing 30-day window and process"</span>
}

<span class="hljs-comment"># Root cause: Retrieval returning wrong docs</span>
retrieved_docs = retrieve_docs(failing_case[<span class="hljs-string">'question'</span>], top_k=<span class="hljs-number">5</span>)
<span class="hljs-comment"># Docs about "payment processing" ranked higher than "refund policy"</span>

<span class="hljs-comment"># Solution 1: Improve retrieval with reranking</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">retrieve_docs_v2</span>(<span class="hljs-params">question: str, top_k: int</span>) -&gt; str:</span>
    <span class="hljs-comment"># Initial retrieval</span>
    candidates = vector_search(question, top_k=<span class="hljs-number">20</span>)

    <span class="hljs-comment"># Rerank by relevance</span>
    reranked = rerank_by_relevance(question, candidates)

    <span class="hljs-keyword">return</span> reranked[:top_k]

<span class="hljs-comment"># Solution 2: Update prompt to require specificity</span>
prompt_v2 = <span class="hljs-string">f"""You are a helpful customer support agent.

Context: <span class="hljs-subst">{context}</span>

Question: <span class="hljs-subst">{question}</span>

Provide a clear, accurate answer based on the context. Include specific details like:
- Time windows (e.g., "within 30 days")
- Step-by-step processes
- Relevant links or contact methods

Answer:"""</span>

<span class="hljs-comment"># Re-evaluate</span>
new_response = answer_support_question_v2(failing_case[<span class="hljs-string">'question'</span>])
new_scores = evaluate_response(
    failing_case[<span class="hljs-string">'question'</span>],
    new_response,
    [<span class="hljs-string">"30 days"</span>, <span class="hljs-string">"full refund"</span>, <span class="hljs-string">"contact support"</span>]
)

<span class="hljs-comment"># Verify improvement</span>
<span class="hljs-keyword">assert</span> new_scores[<span class="hljs-string">'contains_key_info'</span>] == <span class="hljs-literal">True</span>
<span class="hljs-keyword">assert</span> new_scores[<span class="hljs-string">'helpfulness'</span>] &gt;= <span class="hljs-number">4</span>
</code></pre>
<p><strong>How this works:</strong> In this example, the chatbot's refund answer was too vague. After checking what went wrong, the problem was that the system retrieved docs about payment processing instead of the refund policy.</p>
<p>To resolve this, two changes can be made. First, retrieval is improved by grabbing twenty documents, then picking the best five. Second, the prompt is updated to ask for specific details like dates and steps.</p>
<p>After making these changes, the test runs again to confirm it works: the response now has all the key info and scores at least 4 out of 5. This process turns problems into fixes you can measure.</p>
<h3 id="heading-step-5-automate-and-repeat">Step 5: Automate and Repeat</h3>
<p>Integrate evaluation into your development workflow using CI/CD:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># .github/workflows/eval.yml</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">Continuous</span> <span class="hljs-string">Evaluation</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">pull_request:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">main</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">evaluate:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">evaluation</span> <span class="hljs-string">suite</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">python</span> <span class="hljs-string">run_evals.py</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Check</span> <span class="hljs-string">pass</span> <span class="hljs-string">rate</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          PASS_RATE=$(python calculate_pass_rate.py)
          if (( $(echo "$PASS_RATE &lt; 0.85" | bc -l) )); then
            echo "Pass rate $PASS_RATE below threshold"
            exit 1
          fi
</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Upload</span> <span class="hljs-string">results</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/upload-artifact@v2</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">eval-results</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">results/</span>
</code></pre>
<p><strong>Explanation:</strong> This GitHub Actions workflow automates your evaluation process so it runs automatically on every code change. The workflow triggers whenever someone opens a pull request or pushes code to the main branch. It checks out your code, runs your full evaluation suite using <code>run_</code><a target="_blank" href="http://evals.py"><code>evals.py</code></a>, then calculates what percentage of test cases passed. If the pass rate drops below 85%, the workflow fails and blocks the code from being merged, preventing quality regressions from reaching production.</p>
<p><strong>Key practices for automation:</strong></p>
<ul>
<li><p><strong>Version your test cases</strong> — Track them in Git alongside code</p>
</li>
<li><p><strong>Set quality gates</strong> — Block deployments if pass rate drops below threshold</p>
</li>
<li><p><strong>Monitor trends</strong> — Track metrics over time to catch gradual drift</p>
</li>
<li><p><strong>Alert on regressions</strong> — Notify team when specific test cases start failing</p>
</li>
<li><p><strong>Sample production traffic</strong> — Continuously add real queries to eval dataset</p>
</li>
</ul>
<h2 id="heading-tools-and-frameworks-you-can-use-for-evaluation">Tools and Frameworks you can use for evaluation</h2>
<p>Several platforms can help implement continuous evaluation. The one you choose depends on your stack and needs:</p>
<p><strong>If you're building with LLMs:</strong> Try LangSmith or Braintrust first. Both handle prompt versioning, evaluation datasets, and tracing out of the box.</p>
<p><strong>If you're doing traditional ML:</strong> Weights &amp; Biases is the industry standard. If you're in the Microsoft ecosystem, PromptFlow integrates well with Azure.</p>
<p><strong>If you want full control:</strong> Build custom with pytest for test execution and MLflow for tracking results. More setup, but you own the entire pipeline</p>
<h2 id="heading-what-a-complete-evaluation-loop-looks-like-in-practice">What a Complete Evaluation Loop Looks Like in Practice</h2>
<p>This walkthrough shows how a support chatbot improves after running a single cycle of evaluations. Each stage shows how evaluation signals guide improvements and lock in quality for the next release.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Stage</td><td>Before</td><td>After</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Test Case</strong></td><td>"Can I use your API on the free plan?"</td><td>Same question</td></tr>
<tr>
<td><strong>Model Response</strong></td><td>"Yes, you can access our API."</td><td>"Yes, you can access our API on the free plan with a rate limit of 100 requests per day. For higher limits, upgrade to Pro or Enterprise."</td></tr>
<tr>
<td><strong>Evaluation Scores</strong></td><td>contains_key_info=False, helpfulness=2/5</td><td>contains_key_info=True, helpfulness=5/5</td></tr>
<tr>
<td><strong>Issue Identified</strong></td><td>Missing crucial detail: free plan rate limits</td><td>N/A (issue resolved)</td></tr>
<tr>
<td><strong>Analysis / Root Cause</strong></td><td>Retrieval returned general API docs; prompt didn’t emphasize limitations</td><td>N/A (analysis led to fix)</td></tr>
<tr>
<td><strong>Fixes Applied</strong></td><td>1. Improved retrieval to fetch plan comparison docs2. Updated prompt: "Always mention plan-specific restrictions"3. Added validation: Response must mention rate limits if asked</td><td>N/A (fix implemented)</td></tr>
<tr>
<td><strong>Outcome</strong></td><td>Test failed, regression not prevented</td><td>Test passes, regression prevented</td></tr>
<tr>
<td><strong>Next Cycle Actions</strong></td><td>N/A</td><td>1. Add this test case to permanent suite 2. Look for similar issues (other plan-related questions) 3. Monitor production queries for this pattern</td></tr>
</tbody>
</table>
</div><p><strong>Next cycle:</strong></p>
<ul>
<li><p>Add this test case to permanent suite</p>
</li>
<li><p>Look for similar issues (other plan-related questions)</p>
</li>
<li><p>Monitor if this pattern appears in production queries</p>
</li>
</ul>
<h2 id="heading-key-takeaways">Key Takeaways</h2>
<ul>
<li><p><strong>AI systems need continuous evaluation, not one-time testing</strong> — Models drift, data changes, and silent failures accumulate without ongoing checks.</p>
</li>
<li><p><strong>Build evaluation into your workflow from day one</strong> — Don't wait until production failures force you to retrofit evaluation.</p>
</li>
<li><p><strong>Start simple, then scale</strong> — Begin with 10-20 test cases and basic metrics. Grow your suite as you encounter edge cases.</p>
</li>
<li><p><strong>Automate what you can, involve humans for what you can't</strong> — Use programmatic checks for speed, SME review for nuance.</p>
</li>
<li><p><strong>Treat evaluation datasets as first-class artifacts</strong> — Version control them, review changes, and grow them over time.</p>
</li>
<li><p><strong>Make evaluation a team sport</strong> — Product, engineering, and domain experts should all contribute test cases and evaluation criteria.</p>
</li>
</ul>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Every developer has felt the relief of seeing "all tests passing." In AI systems, that reassurance is often misleading. A model can deploy successfully, meet performance benchmarks, and still produce incorrect, incomplete, or misleading outputs in ways traditional tests miss.</p>
<p>The evaluation flywheel addresses this gap by making model behavior testable in practice. Instead of assuming correctness, it forces the system to answer real questions, measures the quality of those answers, and highlights where performance degrades over time. This shifts evaluation from a one-off validation step into an ongoing part of development.</p>
<p>Evaluation won't eliminate uncertainty completely, but it makes failures visible before they reach users. With failures clearly exposed, teams stop guessing and start fixing based on results. This might mean adjusting prompts, improving retrieval logic, or refining evaluation criteria. Over time, this leads to AI systems that evolve in controlled ways rather than breaking silently.</p>
<p><strong>Resources for further reading</strong></p>
<ul>
<li><p><strong>Anthropic's eval guide</strong>: <a target="_blank" href="https://docs.anthropic.com/en/docs/build-with-claude/develop-tests">https://docs.anthropic.com/en/docs/build-with-claude/develop-tests</a></p>
</li>
<li><p><strong>OpenAI's evals framework</strong>: <a target="_blank" href="https://github.com/openai/evals">https://github.com/openai/evals</a></p>
</li>
<li><p><strong>LangChain evaluation</strong>: <a target="_blank" href="https://python.langchain.com/docs/guides/evaluation">https://python.langchain.com/docs/guides/evaluation</a></p>
</li>
<li><p><strong>Arize AI blog</strong>: Comprehensive resources on ML observability</p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use JSON Web Tokens for Secure Authentication in Flask Applications ]]>
                </title>
                <description>
                    <![CDATA[ Passwords, credit card information, personal identification numbers (PINs) – these are all critical assets used for authorization and authentication. This means they need to be protected from unauthorized users. As developers, we are tasked with safe... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/jwt-authentication-in-flask/</link>
                <guid isPermaLink="false">66c37432ad70110156766fe4</guid>
                
                    <category>
                        <![CDATA[ authentication ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Flask Framework ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JSON Web Tokens ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JWT ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Yemi Ojedapo ]]>
                </dc:creator>
                <pubDate>Wed, 17 Apr 2024 16:29:02 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/pexels-soumil-kumar-735911--1-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Passwords, credit card information, personal identification numbers (PINs) – these are all critical assets used for authorization and authentication. This means they need to be protected from unauthorized users.</p>
<p>As developers, we are tasked with safeguarding these sensitive bits of info, and it's important to implement strong secure measures in our applications. </p>
<p>Now, there are many authentication mechanisms available for securing data, like OAuth, OpenID Connect, and JSON Web Tokens (JWTs).</p>
<p>In this article, I'll show you how to use JWTs when securing information in APIs by integrating JWT-based authentication in a Flask application. </p>
<p>Here's what this article will cover:</p>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-a-json-web-token">What is a JSON Web Token?</a></li>
<li><a class="post-section-overview" href="#heading-how-do-jwts-work">How Do JWTs Work?</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-json-web-tokens-to-authenticate-flask-applications">How to Use JSON Web Tokens to Authenticate Flask Applications</a>  </li>
<li><a class="post-section-overview" href="#heading-1-install-the-dependencies">Install dependencies</a>  </li>
<li><a class="post-section-overview" href="#heading-2-create-a-database-and-user-model">Create a database and user model</a>  </li>
<li><a class="post-section-overview" href="#3-configure-the-application-for-jwt-authorization">Configure the application for JWT authentication</a>  </li>
<li><a class="post-section-overview" href="#heading-4-create-protected-routes">Create protected routes</a>  </li>
<li><a class="post-section-overview" href="#heading-5-create-a-login-page">Create a Login Function</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along with this tutorial you will need:</p>
<ul>
<li>An understanding of HTTP Methods</li>
<li>An understanding of how to create APIs in Flask</li>
<li>VS Code (or other similar) editor</li>
<li>A terminal</li>
</ul>
<h2 id="heading-what-is-a-json-web-token">What is a JSON Web Token?</h2>
<p>JSON Web Tokens, or JWTs, are an authentication mechanism used to securely transmit information between a client and a server in JSON format. </p>
<p>This information can be verified and trusted because it is digitally signed with the <a target="_blank" href="https://xilinx.github.io/Vitis_Libraries/security/2020.1/guide_L1/internals/hmac.html">HMAC algorithm</a> or a public/private key pair using <a target="_blank" href="https://en.wikipedia.org/wiki/RSA_(cryptosystem)">RSA</a> or <a target="_blank" href="https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm">ECDSA</a>. </p>
<p>The tokens are encoded into three parts, each divided by a period, like this:</p>
<pre><code>Header.Payload.Signature
</code></pre><ul>
<li><strong>Header:</strong> This defines the type of token (JWT) and the signing algorithm used.</li>
<li><strong>Payload:</strong> This carries user-specific data like user ID, username, roles, and any additional claims you want to include. This payload is encoded in Base64 for maximum security.</li>
<li><strong>Signature:</strong> This is a hashed combination of the header, the payload, and the server's secret key. It ensures the token's integrity and that any modifications to the token will be detected.</li>
</ul>
<h2 id="heading-how-do-jwts-work">How Do JWTs Work?</h2>
<p>To understand how JWTs work, you need to know what the tokens are meant to do. JWTs are not created to hide data but to ensure that the data being sent is authenticated. This is why the JWT is signed and encoded, not encrypted.</p>
<p>A JWT acts as a stateless means of transmitting data from a client to a server. This means that it doesn't store any session object in the browser, so the browser doesn't maintain a session state between requests. </p>
<p>Instead, JWTs use a token that is sent in a request header each time a request is made. This token confirms that the token sent is authenticated and is allowed access to make that request.  </p>
<p>Let's look at how this happens:</p>
<ol>
<li>A user attempts to log in and sends a username and password to be verified by the server.</li>
<li>The verification function carries out a check to see if there's a match in the database.</li>
<li>A JWT is then generated by the server once the user is successfully authenticated (logged in) using their information (payload), such as user ID or username, and signs it using a secret key.</li>
<li>The generated JWT is sent along as a bearer token with every request header to check if the user is authenticated to make that request.</li>
</ol>
<h2 id="heading-how-to-use-json-web-tokens-to-authenticate-flask-applications">How to Use JSON Web Tokens to Authenticate Flask Applications</h2>
<p>To demonstrate how you can implement JWT authentication in Flask, we'll create a simple application that uses JWT for handling login functions and accessing protected routes.</p>
<h3 id="heading-1-install-the-dependencies">1. Install the dependencies</h3>
<p>Run this command to install the dependencies we'll need</p>
<pre><code>pip install flask flask-bcrypt Flask-JWT-Extended
</code></pre><p>Next, make sure you import the dependencies and initialize your Flask application with this code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, jsonify, session, request, redirect, url_for
<span class="hljs-keyword">from</span> flask_jwt_extended <span class="hljs-keyword">import</span> JWTManager, create_access_token, jwt_required, get_jwt_identity, get_jwt


app = Flask(__name__)

////WRITE MAIN CODE HERE


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    <span class="hljs-keyword">with</span> app.app_context():
        app.run(debug=<span class="hljs-literal">True</span>)
</code></pre>
<h3 id="heading-2-create-a-database-and-user-model">2. Create a database and User Model</h3>
<p>To do this, we'll use SQL-Alchemy, which is a Python SQL toolkit that makes it less complex to use SQL in Python scripts. </p>
<p>To set up SQL Alchemy in your application, follow these steps:</p>
<p>First, open your terminal or command prompt and enter the following command:</p>
<pre><code>pip install sqlalchemy
</code></pre><p>This command installs SQLAlchemy in your Python environment, making it available in your project directory.</p>
<p>Next, configure your application to make use of your preferred Database Management System (DBMS). This tutorial will use the SQlite3 DBMS as it doesn't require a separate server:</p>
<pre><code>app.config[<span class="hljs-string">'SQLALCHEMY_DATABASE_URI'</span>] = <span class="hljs-string">'sqlite:///site.db'</span>
</code></pre><p>This code snippet instructs Flask-SQLAlchemy to create and use the <code>site.db</code> file in your project directory as the SQLite database for the application.</p>
<p>Then initialize the database in your application:</p>
<pre><code>db = SQLAlchemy(app)
</code></pre><p>This instance of the database acts as a bridge between the application and the database.</p>
<p>Now create the User Model where we'll store the user's details in this tutorial:</p>
<pre><code><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span>(<span class="hljs-title">db</span>.<span class="hljs-title">Model</span>, <span class="hljs-title">UserMixin</span>):
    <span class="hljs-title">id</span> </span>= db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(<span class="hljs-number">20</span>), unique=True, nullable=False)
    password = db.Column(db.String(<span class="hljs-number">80</span>), nullable=False)
    is_active = db.Column(db.Boolean(), <span class="hljs-keyword">default</span>=True)
    cart = db.Column(<span class="hljs-built_in">JSON</span>, nullable=True, <span class="hljs-keyword">default</span>=list)  # Make cart nullable

    # Define the relationship between User and CartProducts
    cart_products = relationship(<span class="hljs-string">'CartProducts'</span>, backref=<span class="hljs-string">"user"</span>, lazy=<span class="hljs-string">"dynamic"</span>)
    # Define the relationship between User and Wishlists
    wishlists = db.relationship(<span class="hljs-string">'Wishlists'</span>, backref=<span class="hljs-string">'user'</span>, lazy=True)

    def __repr__(self):
        <span class="hljs-keyword">return</span> f<span class="hljs-string">'&lt;User {self.username}&gt;'</span>
</code></pre><p> <strong>Note:</strong> You can create other models using the same syntax to represent different data entities in your application.</p>
<h3 id="heading-3-configure-the-application-for-jwt-authentication">3. Configure the application for JWT Authentication</h3>
<p>To implement JWT authentication in your Flask application, import the necessary libraries and set up the appropriate configurations with this code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, jsonify, request
<span class="hljs-keyword">from</span> flask_sqlalchemy <span class="hljs-keyword">import</span> SQLAlchemy
<span class="hljs-keyword">from</span> flask_jwt_extended <span class="hljs-keyword">import</span> JWTManager, create_access_token, jwt_required

app = Flask(__name__)

<span class="hljs-comment"># Configuration</span>
app.config[<span class="hljs-string">'SECRET_KEY'</span>] = <span class="hljs-string">'your_strong_secret_key'</span>
app.config[<span class="hljs-string">"JWT_SECRET_KEY"</span>] = <span class="hljs-string">'your_jwt_secret_key'</span>
app.config[<span class="hljs-string">'JWT_TOKEN_LOCATION'</span>] = [<span class="hljs-string">'headers'</span>]

<span class="hljs-comment"># Database Initialization</span>
db = SQLAlchemy(app)

<span class="hljs-comment"># JWT Initialization</span>
jwt = JWTManager(app)

<span class="hljs-comment"># Rest of the application code (routes, etc.)</span>
</code></pre>
<p>This code snippet imports the following components needed for our application:</p>
<ul>
<li><strong>app.config['SECRET_KEY']</strong> sets the Flask application's secret key which is used to securely sign session cookies and other security-related needs.</li>
<li><strong>app.config['JWT_SECRET_KEY']</strong> sets the secret key used to encode and decode JWTs in for Flask-JWT operations.</li>
<li><strong>app.config['JWT_TOKEN_LOCATION']</strong> specifies where the application should look for the JWT. Here it's set to look in the HTTP headers.</li>
</ul>
<p>Once we've set this up, we can create the endpoints and routes that we intend to protect.</p>
<h3 id="heading-4-create-protected-routes">4. Create protected routes</h3>
<p>Protected routes are the pages that we intend to keep hidden from unauthorized users. </p>
<p>For example, let's assume we are trying to get into a venue that's exclusive to members of a society. But a guard is securing the venue from "unauthorized users" like us. In this situation, we are the application's users, the venue is the URL we are protecting, and the guard protecting the venue is a <strong><code>@jwt_required</code></strong> decorator.</p>
<p>The <strong><code>@jwt_required</code></strong> decorator is used to protect specific routes that require authentication. This decorator will confirm that there's a JWT access token in the request headers before allowing access to the page:</p>
<pre><code class="lang-python"><span class="hljs-meta">@app.route('/get_name', methods=['GET'])</span>
<span class="hljs-meta">@jwt_required()</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_name</span>():</span>
    <span class="hljs-comment"># Extract the user ID from the JWT</span>
    user_id = get_jwt_identity()
    user = User.query.filter_by(id=user_id).first()

    <span class="hljs-comment"># Check if user exists</span>
    <span class="hljs-keyword">if</span> user:
        <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'message'</span>: <span class="hljs-string">'User found'</span>, <span class="hljs-string">'name'</span>: user.name})
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'message'</span>: <span class="hljs-string">'User not found'</span>}), <span class="hljs-number">404</span>
</code></pre>
<p>In this code snippet, we created a function that returns the name of the user after authentication. If the token is missing, invalid, or expired, the request will be denied, and typically, the server will return an HTTP <strong>401</strong> Unauthorized status.</p>
<h3 id="heading-5-create-a-login-page">5. Create a Login Page</h3>
<p>In this endpoint, we'll create a function that accepts username and password credentials from the client request (for example, form data) and compares the credentials gotten from the user with the user data in the database. If there's a match, a JWT access token will be generated containing the user's information.</p>
<pre><code class="lang-python"><span class="hljs-meta">@app.route('/login', methods=['POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">login</span>():</span>
    data = request.get_json()
    username = data[<span class="hljs-string">'username'</span>]
    password = data[<span class="hljs-string">'password'</span>]
    print(<span class="hljs-string">'Received data:'</span>, username , password)

    user = User.query.filter_by(username=username).first()

    <span class="hljs-keyword">if</span> user <span class="hljs-keyword">and</span> bcrypt.check_password_hash(user.password, password):
        access_token = create_access_token(identity=user.id)
        <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'message'</span>: <span class="hljs-string">'Login Success'</span>, <span class="hljs-string">'access_token'</span>: access_token})
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'message'</span>: <span class="hljs-string">'Login Failed'</span>}), <span class="hljs-number">401</span>
</code></pre>
<p>In this example, the function checks the user's credentials against the database using bcrypt for secure password verification when a POST request is received. If the credentials are valid, the server generates a JWT for the user, allowing them to access protected routes.</p>
<p>Here's an example of a React form sending a POST request to the login endpoint:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</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> Footer <span class="hljs-keyword">from</span> <span class="hljs-string">"./Footer"</span>;
<span class="hljs-comment">// import "./Login.css";</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Login</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [password, setPassword] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [username, setUsername] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">const</span> handleLogin = <span class="hljs-keyword">async</span> (event) =&gt; {
    event.preventDefault();
    <span class="hljs-keyword">const</span> data = {
      <span class="hljs-attr">username</span>: username,
      <span class="hljs-attr">Password</span>: password,
    };

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.post(<span class="hljs-string">"http://localhost:5000/login"</span>, {
        username,
        password,
      });
      <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">"access_token"</span>, response.data.access_token);
      <span class="hljs-comment">// Redirect to protected route</span>
      alert(<span class="hljs-string">"Login successful"</span>);
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">console</span>.error(error);
      <span class="hljs-comment">// Display error message to user</span>
    }
  };

  <span class="hljs-keyword">const</span> handleUsernameChange = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    setUsername(event.target.value);
  };

  <span class="hljs-keyword">const</span> handlePasswordChange = <span class="hljs-function">(<span class="hljs-params">event</span>) =&gt;</span> {
    setPassword(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> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span> &gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
                <span class="hljs-attr">id</span>=<span class="hljs-string">""</span>
                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Username"</span>
                <span class="hljs-attr">name</span>=<span class="hljs-string">"username"</span>
                <span class="hljs-attr">required</span>
                <span class="hljs-attr">value</span>=<span class="hljs-string">{username}</span>
                <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleUsernameChange}</span>
              /&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
                <span class="hljs-attr">id</span>=<span class="hljs-string">""</span>
                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your email"</span>
              /&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span>
                <span class="hljs-attr">required</span>
                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your Password"</span>
                <span class="hljs-attr">name</span>=<span class="hljs-string">"password"</span>
                <span class="hljs-attr">value</span>=<span class="hljs-string">{password}</span>
                <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handlePasswordChange}</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">button</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleLogin}</span>&gt;</span>
            Log In
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>

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

}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Login;
</code></pre>
<p>In this React component, we provided a login form that uses Axios to send a POST request to the login endpoint. It manages username and password inputs using React's <code>useState</code> hook and submits these values once the form is submitted. </p>
<p>If the login is successful, it stores a JWT in local Storage. This allows the client-side application to retrieve the token easily when making authenticated requests to the server.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/jwtDemo-1.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, we've learned how to secure APIs with JSON Web Tokens in Flask. We covered the basics of JWTs, how they work, and provided a step-by-step process for implementing this method of authentication. This included everything from installing necessary dependencies to creating user models and protecting routes. </p>
<p>You can build upon this foundation, such as by adding refresh tokens, integrating with third-party OAuth providers, or handling more complex user permissions.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Idempotence in HTTP Methods – Explained with CRUD Examples ]]>
                </title>
                <description>
                    <![CDATA[ Idempotence refers to a program's ability to maintain a particular result even after repeated actions.  For example, let's say you have a button that only opens a door when pressed. This button does not have the ability to close the door, so it stays... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/idempotency-in-http-methods/</link>
                <guid isPermaLink="false">66c3742fad70110156766fe2</guid>
                
                    <category>
                        <![CDATA[ http ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Yemi Ojedapo ]]>
                </dc:creator>
                <pubDate>Fri, 22 Dec 2023 21:19:43 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/12/pexels-robert-lens-10382808.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Idempotence refers to a program's ability to maintain a particular result even after repeated actions. </p>
<p>For example, let's say you have a button that only opens a door when pressed. This button does not have the ability to close the door, so it stays open even when it's pressed repeatedly. It simply remains in the state it was changed to by the first press.</p>
<p>This same logic applies to HTTP methods that are idempotent. Operating on idempotent HTTP methods repeatedly won't have any additional effect beyond the initial execution. </p>
<p>Understanding idempotence is important for maintaining the consistency of HTTP methods and API design. Idempotence has a significant impact on API design, as it influences how API endpoints should behave when processing requests from clients. </p>
<p>In this tutorial, I'll explain the concept of idempotence and the role it plays in building robust and functional APIs. You'll also learn about what safe methods are, how they relate to idempotence, and how to implement idempotency in non-idempotent methods. </p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before understanding and implementing idempotence in API design, it's essential to have a solid foundation in the following areas:</p>
<ul>
<li>RESTful Principles</li>
<li>Fundamentals of HTTP methods</li>
<li>API Development </li>
<li>HTTP Status codes</li>
<li>Basics of Web development.</li>
</ul>
<h2 id="heading-idempotence-example">Idempotence Example</h2>
<p>Let's start off with an example of idempotence in action. We'll create a function that uses the DELETE method to delete data from a web page:</p>
<pre><code class="lang-python">
<span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, jsonify, abort

app = Flask(__name__)

web_page_data = [
   {<span class="hljs-string">"id"</span>: <span class="hljs-number">1</span>, <span class="hljs-string">"content"</span>: <span class="hljs-string">"Row 1 data"</span>},
   {<span class="hljs-string">"id"</span>: <span class="hljs-number">2</span>, <span class="hljs-string">"content"</span>: <span class="hljs-string">"Row 2 data"</span>},
   <span class="hljs-comment"># Add more rows as needed</span>
]

<span class="hljs-meta">@app.route('/delete_row/&lt;int:row_id&gt;', methods=['DELETE'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete_row</span>(<span class="hljs-params">row_id</span>):</span>
   <span class="hljs-comment"># Find the row to delete</span>
   row_to_delete = next((row <span class="hljs-keyword">for</span> row <span class="hljs-keyword">in</span> web_page_data <span class="hljs-keyword">if</span> row[<span class="hljs-string">"id"</span>] == row_id), <span class="hljs-literal">None</span>)

   <span class="hljs-keyword">if</span> row_to_delete:
       <span class="hljs-comment"># Simulate deletion</span>
       web_page_data.remove(row_to_delete)
       <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">"message"</span>: <span class="hljs-string">f"Row <span class="hljs-subst">{row_id}</span> deleted successfully."</span>}), <span class="hljs-number">200</span>
   <span class="hljs-keyword">else</span>:
       abort(<span class="hljs-number">404</span>, description=<span class="hljs-string">f"Row <span class="hljs-subst">{row_id}</span> not found."</span>)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
   app.run(debug=<span class="hljs-literal">True</span>)
</code></pre>
<p>This function is expected to delete the rows chosen by the user. Now because of the idempotent nature of the DELETE method, the data will be deleted once, even when called repeatedly. But subsequent calls will return a 404 error since the data has already been deleted by the first call.  </p>
<p>Let’s look at another example with the GET method. The GET method is used to retrieve data from a resource. Let’s create a function that uses the GET method to retrieve a username:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> requests

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_username</span>():</span>
    url = <span class="hljs-string">'https://api.example.com/get_username'</span>
    <span class="hljs-keyword">try</span>:
        response = requests.get(url)
        <span class="hljs-keyword">if</span> response.status_code == <span class="hljs-number">200</span>:
            <span class="hljs-keyword">return</span> response.json()[<span class="hljs-string">'username'</span>]
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
    <span class="hljs-keyword">except</span> requests.RequestException <span class="hljs-keyword">as</span> e:
        print(<span class="hljs-string">f"Error occurred: <span class="hljs-subst">{e}</span>"</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>

<span class="hljs-comment"># Usage</span>
username = get_username()
<span class="hljs-keyword">if</span> username:
    print(<span class="hljs-string">f"The username is: <span class="hljs-subst">{username}</span>"</span>)
<span class="hljs-keyword">else</span>:
    print(<span class="hljs-string">"Failed to retrieve the username."</span>)
</code></pre>
<p>In this example, we define the <code>get_username()</code> function, which sends a GET request to the API endpoint to retrieve the username. If the request is successful, we extract the username from the JSON response and return it. But if any error occurs during the request, we handle it and return <code>None</code>.</p>
<p>Now the idempotent nature of the GET method ensures that even if you call <code>get_username()</code> multiple times, the same username will be fetched from the API each time. The result will always be the same which is to fetch the username from the resource.</p>
<h3 id="heading-idempotent-vs-non-idempotent-http-methods">Idempotent vs. Non-Idempotent HTTP Methods:</h3>
<p>HTTP methods play crucial roles in determining how data is fetched, modified, or created when interacting with APIs. And Idempotency is one of the important concepts that influences data consistency and reliability in the methods used . </p>
<p>Here's a breakdown of the different methods based on their idempotency.</p>
<h4 id="heading-idempotent-methods">Idempotent methods:</h4>
<ul>
<li>GET</li>
<li>HEAD</li>
<li>PUT</li>
<li>DELETE</li>
<li>OPTIONS</li>
<li>TRACE</li>
</ul>
<h4 id="heading-non-idempotent-methods">Non-idempotent methods:</h4>
<ul>
<li>POST</li>
<li>PATCH</li>
<li>CONNECT</li>
</ul>
<h2 id="heading-safe-methods">Safe Methods</h2>
<p>In our previous example, we used the GET method to retrieve a username and this had no side effect on the server. This is because it is a safe method. </p>
<p>A safe method is a type of method that doesn’t modify the server’s state or the resource being accessed. In other words, they perform read-only operations used to retrieve data or for resource representation.</p>
<p>When you make a request using a safe method, the server does not perform any operations that modify the resource's state. Like in our previous example, we retrieved the username from the webpage which is the resource without changing anything in the server. </p>
<p>All safe methods are automatically idempotent, but not all idempotent methods are safe. This is because while idempotent methods produce consistent results when called repeatedly, some of them may still modify the server's state or the resource being accessed.</p>
<p>Like in our first example, the DELETE method is idempotent, because deleting a resource multiple times will have the same effect. But it's not safe, as it changes the server's state by removing the resource.</p>
<p>Here’s a classification of HTTP methods based on their safe status:</p>
<h4 id="heading-safe-methods-1">Safe methods:</h4>
<ul>
<li>GET</li>
<li>OPTIONS</li>
<li>HEAD</li>
</ul>
<h4 id="heading-unsafe-methods">Unsafe methods:</h4>
<ul>
<li>DELETE</li>
<li>POST</li>
<li>PUT</li>
<li>PATCH</li>
</ul>
<h3 id="heading-why-is-post-not-idempotent">Why is POST not idempotent?</h3>
<p>POST is an HTTP method that sends information to a server. When you make a POST request, you typically submit data to create a new resource or trigger a server-side action. Therefore, making the same request multiple times can result in different outcomes and side effects on the server. This can lead to duplicated data, starting server resources, and reducing performance because of the repeated action.</p>
<p>Unlike idempotent methods like GET, PUT, and DELETE, which have consistent results regardless of repetition, POST requests can cause changes to the server's state with each invocation. </p>
<p>POST requests often create new resources on the server. Repeating the same POST request will generate multiple identical resources, potentially leading to duplication.</p>
<p>This is similar to DELETE which is an idempotent method but not a safe method. Deleting the last entry in a collection using a single DELETE request would be considered idempotent. But if a developer creates a function that deletes the last entry, that would trigger DELETE multiple times. Subsequent DELETE calls would have different effects, as each one removes a unique entry. This would be considered non-idempotent.</p>
<h2 id="heading-how-to-achieve-idempotency-with-non-idempotent-methods">How to Achieve Idempotency with Non-Idempotent Methods</h2>
<p>Idempotency isn't only a property inherent to certain methods – it can also be implemented as a feature of a non-idempotent method.  </p>
<p>Here are some techniques to achieve idempotency even with non-idempotent methods.</p>
<h3 id="heading-unique-identifiers">Unique Identifiers</h3>
<p>Adding unique identifiers to every request is one of the most common techniques used to implement idempotency. It works by tracking whether the operation has already been performed or not. If it's a duplicate (a repeat request), the server knows it's already dealt with that request and simply ignores it, ensuring that no side effects occur. </p>
<p>Here's an example of how it works:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> uuid <span class="hljs-keyword">import</span> uuid4

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_order</span>(<span class="hljs-params">unique_id, order_data</span>):</span>
    <span class="hljs-keyword">if</span> Order.objects.filter(unique_id=unique_id).exists():
        <span class="hljs-keyword">return</span> HttpResponse(status=<span class="hljs-number">409</span>)  <span class="hljs-comment"># Conflict</span>
    order = Order.objects.create(unique_id=unique_id, **order_data)
    <span class="hljs-keyword">return</span> HttpResponse(status=<span class="hljs-number">201</span>, content_type=<span class="hljs-string">"application/json"</span>)

<span class="hljs-comment"># Example usage</span>
post_data = {<span class="hljs-string">"products"</span>: [...]}
headers = {<span class="hljs-string">"X-Unique-ID"</span>: str(uuid4())}
requests.post(<span class="hljs-string">"https://api.example.com/orders"</span>, data=post_data, headers=headers)
</code></pre>
<p>In this code snippet, we define a function called <code>process_order</code> that creates orders in an API, using unique identifiers to implement idempotency. </p>
<p>Here's a breakdown of the code:</p>
<h4 id="heading-importing-the-unique-identifier-generator">Importing the Unique Identifier Generator:</h4>
<p><code>from uuid import uuid4</code>: The code snippet starts by importing the <code>uuid4</code> function from the <code>uuid</code> module. This function generates unique identifiers, which are used to achieve idempotency in this code.</p>
<h4 id="heading-defining-the-processorder-function">Defining the <code>process_order</code> Function:</h4>
<p><code>def process_order(unique_id, order_data)</code>: This line defines a function named process_order that takes two arguments:</p>
<ul>
<li><code>unique_id</code>: This is a string representing a unique identifier for the request. This ensures no duplicate orders are created with the same identifier.</li>
<li><code>order_data</code>: This is a dictionary containing the actual order data, like product information and customer details.</li>
</ul>
<h4 id="heading-checking-for-existing-orders">Checking for Existing Orders:</h4>
<p><code>if Order.objects.filter(unique_id=unique_id).exists()</code>: This line checks if an order with the same unique_id already exists in the database.</p>
<p><code>Order.objects.filter(unique_id=unique_id).exists()</code> queries the Order model for orders with the matching unique_id and checks if any orders were found in the query result. If an order is found, it means the same request was already processed.</p>
<h4 id="heading-handling-existing-orders">Handling existing orders:</h4>
<p><code>return HttpResponse(status=409)</code>: If an order with the same unique_id already exists, the function immediately returns an HTTP response with status code 409 indicating a conflict. This prevents duplicate orders from being created.</p>
<h4 id="heading-creating-a-new-order-if-unique">Creating a new order (if unique):</h4>
<p><code>order = Order.objects.create(unique_id=unique_id, **order_data )</code>: This line only runs if no existing order is found.</p>
<p><code>Order.objects.create:</code> creates a new object in the Order model.</p>
<p><code>unique_id=unique_id</code> sets the unique_id attribute of the new order to the provided unique_id.</p>
<p><code>order_data</code>: spreads the dictionary order_data as keyword arguments to the order model's constructor, setting other relevant attributes like products and customer information.</p>
<h4 id="heading-sending-a-success-response">Sending a success response:</h4>
<p><code>return HttpResponse(status=201, content_type="application/json")</code>: If the order creation is successful, the function will return an HTTP response with status code 201 which shows a successful creation. It also specifies the response content type as JSON, assuming the order data might be returned in JSON format.</p>
<p><code>post_data = {"products": [...]}</code>: an example request, defines a dictionary containing the actual order data, like a list of products.</p>
<p><code>headers = {"X-Unique-ID": str(uuid4())}</code>: This line creates a dictionary containing a custom header named X-Unique-ID. It generates a unique identifier string using uuid4() and adds it to the header.</p>
<p><code>requests.post("https://api.example.com/orders", data=post_data, headers=headers</code>): This line sends a POST request to the API endpoint <code>https://api.example.com/orders</code>  with the provided <code>post_data</code> and headers.</p>
<h4 id="heading-how-does-this-implement-idempotence">How does this implement idempotence?</h4>
<p>It does so by using a unique identifier <code>(unique_id)</code> for each order. </p>
<p>It checks if an order with the same identifier already exists in the database. If it returns true, it returns a 409 Conflict status. Otherwise, it creates a new order and responds with a 201 Created status. The unique identifier prevents duplicate orders, making the system idempotent.</p>
<h3 id="heading-token-based-authorization">Token-based Authorization</h3>
<p>Token-based authorization is a form of authorization that assigns temporary tokens for each non-idempotent action. Once the action is completed, the token is invalidated. If the same request comes again with the same token, the server recognizes it as invalid and refuses the request, thereby preventing duplicate actions.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Generate a unique token for this action</span>
<span class="hljs-keyword">const</span> token = generateToken();

fetch(<span class="hljs-string">"https://api.example.com/create-user"</span>, {
    <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
    <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({ username, password }),
    <span class="hljs-attr">headers</span>: {
        <span class="hljs-attr">Authorization</span>: <span class="hljs-string">`Bearer <span class="hljs-subst">${token}</span>`</span>,
        <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
    },
})
    .then(<span class="hljs-function"><span class="hljs-params">response</span> =&gt;</span> {
        <span class="hljs-comment">// Handle successful response</span>
        <span class="hljs-keyword">if</span> (response.ok) {
            <span class="hljs-comment">// Do something with the successful response</span>
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// Handle non-successful response</span>
        }
    })
    .catch(<span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {
        <span class="hljs-comment">// Handle error</span>
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error occurred:"</span>, error);
    })
    .finally(<span class="hljs-function">() =&gt;</span> {
        <span class="hljs-comment">// Invalidate token after successful action or in case of an error</span>
        invalidateToken(token);
    });

<span class="hljs-comment">// Simple implementation for generating a token</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generateToken</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.random().toString(<span class="hljs-number">36</span>).substr(<span class="hljs-number">2</span>);
}

<span class="hljs-comment">// Simple implementation for invalidating a token</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">invalidateToken</span>(<span class="hljs-params">token</span>) </span>{
    <span class="hljs-comment">// Add your logic to invalidate the token, e.g., remove it from storage</span>
}
</code></pre>
<p>Here's a breakdown of the code:</p>
<h4 id="heading-generating-a-unique-token">Generating a unique token:</h4>
<p><code>const token = generateToken()</code>: This line calls a function named <code>generateToken()</code> (which is assumed to be defined elsewhere) that generates a unique token string. This token will be used for authorization and idempotency.</p>
<h4 id="heading-sending-the-post-request">Sending the <code>POST</code> request:</h4>
<p><code>fetch("https://api.example.com/create-user", { ... })</code>: This line uses the fetch API to send a POST request to the API endpoint <code>https://api.example.com/create-user</code>. </p>
<p><code>method: "POST"</code>: This specifies the HTTP method as POST, indicating the intention to create a new user.</p>
<p><code>body: JSON.stringify({ username, password })</code>: This defines the request body with user details like username and password. The data is converted to JSON format before sending.</p>
<p><code>headers: { Authorization:Bearer ${token}}</code>: This sets the Authorization header in the request. The header value includes the generated token prefixed with "Bearer ".</p>
<h4 id="heading-handling-the-response">Handling the Response:</h4>
<p><code>.then(response =&gt; { ... })</code>: This block defines the code to execute if the request is successful. You would handle things like storing user information or redirecting the user upon successful user creation.</p>
<p><code>.catch(error =&gt; { ... }):</code> This block defines the code to execute if the request encounters an error. You would handle any error messages or handle specific error scenarios here.</p>
<h4 id="heading-invalidating-the-token">Invalidating the Token:</h4>
<p><code>invalidateToken(token)</code>: This line calls a function named <code>invalidateToken(token)</code> ( which is assumed to be defined elsewhere) which would likely mark the used token as invalid. This ensures the same token cannot be used for subsequent requests, adding to the idempotency guarantee.</p>
<h4 id="heading-how-does-this-implement-idempotence-1">How does this implement Idempotence?</h4>
<p>This code snippet uses token-based authorization to implement idempotency in a POST request to create a user on an API. If a user creation request is accidentally sent multiple times, a new unique token is generated each time and used in the Authorization header.</p>
<p>The API server can recognize and verify the unique token, and since the user creation action has already been performed (assuming it's successful the first time), it won't create duplicate users due to subsequent identical requests.</p>
<h3 id="heading-etag-header">ETag Header:</h3>
<p>An ETag header (Entity Tag) is an HTTP header used for web cache validation and conditional requests. It is mainly used for  PUT requests, that only update resources if they haven't changed since the last check.</p>
<p>When you want to update a resource, the server sends you its ETag which is then included in your PUT request along with the updated data. If the ETag hasn't changed (meaning the resource remains the same), the server accepts the update. But if the ETag has changed, the server rejects the update, preventing it from overwriting someone else's changes.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update_article</span>(<span class="hljs-params">article_id, content</span>):</span>
    <span class="hljs-comment"># Get existing article and its ETag</span>
    article = Article.objects.get(pk=article_id)
    etag = article.etag

    <span class="hljs-comment"># Check if ETag matches with request header</span>
    <span class="hljs-keyword">if</span> request.headers.get(<span class="hljs-string">"If-Match"</span>) != etag:
        <span class="hljs-keyword">return</span> HttpResponse(status=<span class="hljs-number">409</span>)  <span class="hljs-comment"># Conflict</span>

    <span class="hljs-comment"># Update article content and generate new ETag</span>
    article.content = content
    article.save()
    new_etag = article.etag

    <span class="hljs-comment"># Return success response with updated ETag</span>
    <span class="hljs-keyword">return</span> HttpResponse(status=<span class="hljs-number">200</span>, content_type=<span class="hljs-string">"text/plain"</span>, content=new_etag)
</code></pre>
<p>In this code snippet, we define a function called <code>update_article</code> that allows you to update the content of an existing article based on its ID and new content. It implements idempotency using the ETag header technique.</p>
<p>Here's a step-by-step explanation of how it works;</p>
<h4 id="heading-getting-the-existing-article-and-its-etag">Getting the Existing Article and its ETag:</h4>
<p><code>article = Article.objects.get(pk=article_id):</code> This line fetches the article with the provided article_id from the database using the Article model.</p>
<p><code>etag = article.etag:</code> This line extracts the ETag value from the retrieved article object. The ETag serves as a unique identifier for the article's current state.</p>
<h4 id="heading-checking-for-a-match">Checking for a Match:</h4>
<p><code>if request.headers.get("If-Match") != etag:</code> This line checks if the ETag header provided in the request matches the ETag of the retrieved article.</p>
<p><code>return HttpResponse(status=409)</code>: If the ETag doesn't match, it indicates that the article might have been updated by another request since the client retrieved its information. The function returns a 409 Conflict response, which prevents accidental data corruption.</p>
<h4 id="heading-updating-the-article-content-and-generating-a-new-etag">Updating the Article Content and generating a new ETag:</h4>
<p><code>article.content = content:</code> This line updates the article's content with the new content received in the request.</p>
<p><code>article.save():</code> This line saves the updated article back to the database.</p>
<p><code>new_etag = article.etag:</code> This line retrieves the new ETag generated for the updated article after saving it.</p>
<h4 id="heading-returning-the-success-response-with-the-new-etag">Returning the Success Response with the new ETag:</h4>
<p><code>return HttpResponse(status=200, content_type="text/plain", content=new_etag)</code>: returns a successful 200 OK response, including the content type ("text/plain") and the updated ETag of the article in the response body.</p>
<h4 id="heading-how-does-this-implement-idempotence-2">How does this implement idempotence?</h4>
<p>This code ensures that if the same update request is sent multiple times with the same ETag, the update will only be performed once, preventing duplicate updates and maintaining data consistency. The new ETag is then provided in the response to help the client keep track of the article's state for future interactions.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, we highlighted the difference between safe methods like GET, which retrieves data without side effects, and non-idempotent methods like POST, which can have different outcomes with each repetition. </p>
<p>We also explored techniques you can apply to achieve idempotence in non-idempotent methods, emphasizing the importance of designing APIs that prioritize consistency and reliability.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
