<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Champ of Greatness</title>
    <description>The latest articles on DEV Community by Champ of Greatness (@champcbg).</description>
    <link>https://dev.to/champcbg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1567714%2Fb8236fab-ea08-4ebc-baae-1faf1d8beaf5.jpeg</url>
      <title>DEV Community: Champ of Greatness</title>
      <link>https://dev.to/champcbg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/champcbg"/>
    <language>en</language>
    <item>
      <title>Lifting CQRS and DDD into the Client: A Shared App-Domain for Web and Mobile</title>
      <dc:creator>Champ of Greatness</dc:creator>
      <pubDate>Fri, 27 Feb 2026 02:55:21 +0000</pubDate>
      <link>https://dev.to/champcbg/lifting-cqrs-and-ddd-into-the-client-a-shared-app-domain-for-web-and-mobile-21og</link>
      <guid>https://dev.to/champcbg/lifting-cqrs-and-ddd-into-the-client-a-shared-app-domain-for-web-and-mobile-21og</guid>
      <description>&lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; This post describes why we moved from business rules scattered across a web app in a monorepo to a single &lt;strong&gt;app-domain&lt;/strong&gt; package shared by both web and mobile. We combined &lt;strong&gt;CQRS&lt;/strong&gt; (Command Query Responsibility Segregation) and &lt;strong&gt;DDD&lt;/strong&gt; (Domain-Driven Design)—patterns usually associated with server-side architecture—and ran them &lt;strong&gt;inside the client&lt;/strong&gt;. Here we outline the transformation, how the shared domain works, and the pros and cons of this design.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Business Rules Everywhere
&lt;/h2&gt;

&lt;p&gt;Originally, the product lived as a &lt;strong&gt;web app in a monorepo&lt;/strong&gt;. Business logic was spread across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UI components&lt;/strong&gt; making direct Supabase calls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ad-hoc services&lt;/strong&gt; with validation and persistence mixed together&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicate or inconsistent rules&lt;/strong&gt; as features were copied from web to mobile&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That led to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No single source of truth&lt;/strong&gt; for “what is a valid entity?”, “who can rename a record?”, or “how do we create a reusable template?”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Divergence&lt;/strong&gt; between web and mobile behavior when each client implemented its own checks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard-to-test&lt;/strong&gt; logic tied to React and Supabase, with no clear boundary between “domain” and “infrastructure”&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risky changes&lt;/strong&gt;—touching a rule in one place might miss another client or code path&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We needed one place where &lt;strong&gt;all business rules&lt;/strong&gt; live, and both web and mobile could reuse it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Direction: One Domain, Two Clients
&lt;/h2&gt;

&lt;p&gt;The target was clear: &lt;strong&gt;extract a shared domain and application layer&lt;/strong&gt; that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Holds &lt;strong&gt;all business rules&lt;/strong&gt; (validation, invariants, lifecycle).&lt;/li&gt;
&lt;li&gt;Exposes a &lt;strong&gt;stable API&lt;/strong&gt; (commands and queries) so UI never talks to the database directly.&lt;/li&gt;
&lt;li&gt;Is &lt;strong&gt;client-agnostic&lt;/strong&gt;—the same package runs in the browser (web) and in React Native (mobile).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That shared layer is &lt;strong&gt;app-domain&lt;/strong&gt;: a single package consumed by both the web app and the mobile app, with Supabase (or any persistence) injected at runtime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
  subgraph clients["Clients"]
    WEB[Web App]
    MOB[Mobile App]
  end

  subgraph shared["Shared (single source of truth)"]
    AD[app-domain]
  end

  subgraph infra["Infrastructure"]
    SUP[(Supabase)]
  end

  WEB --&amp;gt; AD
  MOB --&amp;gt; AD
  AD --&amp;gt; SUP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The monorepo keeps &lt;strong&gt;app-domain&lt;/strong&gt;, &lt;strong&gt;web&lt;/strong&gt;, and &lt;strong&gt;mobile&lt;/strong&gt; together today; the &lt;a href="//../REPO_SEPARATION_MIGRATION_PLAN.md"&gt;repo separation plan&lt;/a&gt; moves toward three repos, with web and mobile depending on a &lt;strong&gt;published&lt;/strong&gt; &lt;code&gt;@champcbg/atlas-app-domain&lt;/code&gt; package.&lt;/p&gt;




&lt;h2&gt;
  
  
  CQRS and DDD—Usually Server-Side, Now in the Client
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CQRS&lt;/strong&gt; (Command Query Responsibility Segregation) separates &lt;strong&gt;writes&lt;/strong&gt; (commands) from &lt;strong&gt;reads&lt;/strong&gt; (queries). Handlers execute commands that change state or queries that return data—no mixing of the two at the API boundary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DDD&lt;/strong&gt; (Domain-Driven Design) puts &lt;strong&gt;entities&lt;/strong&gt;, &lt;strong&gt;invariants&lt;/strong&gt;, and &lt;strong&gt;domain events&lt;/strong&gt; at the centre. Persistence is hidden behind &lt;strong&gt;repository interfaces&lt;/strong&gt;; the domain stays pure and testable.&lt;/p&gt;

&lt;p&gt;These patterns are typically used &lt;strong&gt;on the server&lt;/strong&gt;: API layer, domain service, repositories, database. We &lt;strong&gt;lifted them into the client&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Commands&lt;/strong&gt; (e.g. &lt;code&gt;CreateEntity&lt;/code&gt;, &lt;code&gt;UpdateEntity&lt;/code&gt;, &lt;code&gt;DeleteEntity&lt;/code&gt;) and &lt;strong&gt;queries&lt;/strong&gt; (e.g. &lt;code&gt;GetEntitiesForContext&lt;/code&gt;, &lt;code&gt;GetEntityById&lt;/code&gt;) are defined and registered in app-domain.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain entities&lt;/strong&gt; (aggregate roots and value objects) enforce rules and emit domain events; they live in &lt;code&gt;domain/entities/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repositories&lt;/strong&gt; are interfaces in the domain; &lt;strong&gt;Supabase implementations&lt;/strong&gt; live in the app layer (&lt;code&gt;app/infrastructure/supabase/&lt;/code&gt;). The UI never imports Supabase—only &lt;code&gt;executeCommand&lt;/code&gt; and &lt;code&gt;executeQuery&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correlation context&lt;/strong&gt; (e.g. &lt;code&gt;userId&lt;/code&gt;, &lt;code&gt;sessionId&lt;/code&gt;, &lt;code&gt;correlationId&lt;/code&gt;) is created at the UI boundary and passed into every command/query for logging and tracing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So: same &lt;strong&gt;mental model&lt;/strong&gt; as a server-side CQRS/DDD app, but the “server” is the &lt;strong&gt;client process&lt;/strong&gt; (browser or React Native), and the “database” is Supabase called from that process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TB
  subgraph ui["Web or Mobile UI"]
    Page[Page / Screen]
  end

  subgraph app["App Layer (in client)"]
    Dispatch["executeCommand / executeQuery"]
    CmdHandlers[Command Handlers]
    QueryHandlers[Query Handlers]
    EventBus[Event Bus]
    Repos[Repository Implementations]
  end

  subgraph domain["Domain Layer"]
    Entities[Entities + Invariants]
    Events[Domain Events]
    RepoIfaces[Repository Interfaces]
  end

  subgraph persistence["Persistence"]
    Supabase[(Supabase)]
  end

  Page --&amp;gt; Dispatch
  Dispatch --&amp;gt; CmdHandlers
  Dispatch --&amp;gt; QueryHandlers
  CmdHandlers --&amp;gt; Entities
  QueryHandlers --&amp;gt; RepoIfaces
  CmdHandlers --&amp;gt; Repos
  QueryHandlers --&amp;gt; Repos
  Repos --&amp;gt; RepoIfaces
  Repos --&amp;gt; Supabase
  Entities --&amp;gt; Events
  CmdHandlers --&amp;gt; EventBus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  How the Shared App-Domain Is Used
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Bootstrap (Web and Mobile)
&lt;/h3&gt;

&lt;p&gt;Before any command or query runs, the host app &lt;strong&gt;initializes&lt;/strong&gt; app-domain with its own dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initAppDomain&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@champcbg/atlas-app-domain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/lib/supabase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/services/base/logger&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;captureError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@/services/base/errorHandler&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;initAppDomain&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;withContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;captureError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So: &lt;strong&gt;Supabase, logging, and error reporting are injected&lt;/strong&gt;; app-domain does not depend on a specific framework or environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Usage in UI (Web or Mobile)
&lt;/h3&gt;

&lt;p&gt;UI creates a &lt;strong&gt;correlation context&lt;/strong&gt; and calls the same API from either client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;createCorrelationContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;executeCommand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;executeQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;getEntitiesForContextQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;createEntityCommand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-domain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createCorrelationContext&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;listResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;executeQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;getEntitiesForContextQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contextId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;includeArchived&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;executeCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;createEntityCommand&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Example item&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contextId&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;context&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All feature areas in the product go through this same pipeline. &lt;strong&gt;One contract, two platforms.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Pros of This Design
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benefit&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Single source of truth&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All business rules live in domain entities and command/query handlers. No “web version” vs “mobile version” of the same rule.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Testability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Handlers take repository interfaces and logger/captureError; unit tests mock repositories and assert on behavior without Supabase or React.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consistent behavior&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Web and mobile share the same validation, normalization, and error codes (e.g. &lt;code&gt;VALIDATION_ERROR&lt;/code&gt;, &lt;code&gt;ENTITY_NOT_FOUND&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Clear boundaries&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;UI only knows about commands, queries, and DTOs. Persistence and infrastructure stay behind the app layer.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Easier evolution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;New features or rule changes are implemented once in app-domain; both clients get them when they upgrade the package.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Correlation and observability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Every operation carries correlation context, so logs and errors can be traced across the client and (where used) backend.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Domain events&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Entities record events (e.g. &lt;code&gt;RoutineCreated&lt;/code&gt;, &lt;code&gt;GoalCompleted&lt;/code&gt;); the app layer publishes them so the host can react (e.g. create profile, sync external systems) without coupling domain to UI.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Cons and Trade-offs
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trade-off&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Client-side complexity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CQRS/DDD on the client is more structure than a “call Supabase from the component” approach. New developers must learn the command/query/dispatch model and where to add logic (entity vs handler).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bundle size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The full app-domain (entities, handlers, DTOs, repository adapters) is included in each client. We accept this for correctness and consistency; tree-shaking and code-splitting can help where applicable.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No dedicated API server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Commands and queries run in the client and call Supabase (or similar) directly. There is no mandatory “backend for all logic”—which fits our current design but means auth and RLS must be solid and any future server-side-only logic would need a different path (e.g. Edge Functions).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Initialization discipline&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;initAppDomain()&lt;/code&gt; must be called before any &lt;code&gt;executeCommand&lt;/code&gt;/&lt;code&gt;executeQuery&lt;/code&gt;. If forgotten, the app fails at runtime; we mitigate with clear errors and docs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Event bus is in-memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The event bus is process-local. Cross-tab or cross-device events would require a separate mechanism (e.g. Supabase Realtime, or server-driven events).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  When This Approach Fits
&lt;/h2&gt;

&lt;p&gt;This design works well when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have &lt;strong&gt;multiple clients&lt;/strong&gt; (e.g. web and mobile) that must behave the same.&lt;/li&gt;
&lt;li&gt;You want &lt;strong&gt;one place&lt;/strong&gt; to enforce and evolve business rules.&lt;/li&gt;
&lt;li&gt;You are comfortable with &lt;strong&gt;Supabase (or similar) from the client&lt;/strong&gt; and strong &lt;strong&gt;RLS/auth&lt;/strong&gt; rather than a thick API server for every operation.&lt;/li&gt;
&lt;li&gt;You value &lt;strong&gt;testability and clear boundaries&lt;/strong&gt; and are willing to invest in the command/query/entity structure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is less suitable when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need &lt;strong&gt;all&lt;/strong&gt; logic behind a single API server (e.g. strict “no direct DB from client” policies).&lt;/li&gt;
&lt;li&gt;The team prefers &lt;strong&gt;minimal structure&lt;/strong&gt; and “quick UI → Supabase” wiring over a shared domain.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;We transformed from &lt;strong&gt;business rules spread across the web app&lt;/strong&gt; in a monorepo to a &lt;strong&gt;shared app-domain&lt;/strong&gt; used by both web and mobile. By bringing &lt;strong&gt;CQRS and DDD into the client&lt;/strong&gt;, we get a single source of truth for rules, consistent behavior across platforms, and a testable, boundary-conscious design—at the cost of more upfront structure and dependency on correct initialization and packaging. For a product that must stay consistent on web and mobile while evolving quickly, this trade-off has been worthwhile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
  Before[Rules in Web Only] --&amp;gt; Extract[Extract app-domain]
  Extract --&amp;gt; Share[Share with Mobile]
  Share --&amp;gt; CQRS[CQRS + DDD in client]
  CQRS --&amp;gt; Result[One domain, two clients]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Related documentation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="//../APP_DOMAIN_LAYER_CQRS.md"&gt;App-Domain CQRS (Domain + App Layer)&lt;/a&gt; – folder layout, handlers, commands/queries list&lt;/li&gt;
&lt;li&gt;
&lt;a href="//../DOMAIN_FIRST_ENFORCEMENT.md"&gt;Domain-First Enforcement&lt;/a&gt; – entity as source of truth, DB as secondary&lt;/li&gt;
&lt;li&gt;
&lt;a href="//../REPO_SEPARATION_MIGRATION_PLAN.md"&gt;Repo Separation (Tech): Monorepo to Three-Repo Migration&lt;/a&gt; – app-domain, web-app, mobile-app repos and package consumption&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>supabase</category>
      <category>cqrs</category>
      <category>ddd</category>
      <category>typescript</category>
    </item>
    <item>
      <title>How I Built an AI Agent to Run Sprint Planning So I Can Actually Build</title>
      <dc:creator>Champ of Greatness</dc:creator>
      <pubDate>Thu, 26 Feb 2026 14:14:49 +0000</pubDate>
      <link>https://dev.to/champcbg/how-i-built-an-ai-agent-to-run-sprint-planning-so-i-can-actually-build-95h</link>
      <guid>https://dev.to/champcbg/how-i-built-an-ai-agent-to-run-sprint-planning-so-i-can-actually-build-95h</guid>
      <description>&lt;p&gt;I'm a solo entrepreneur. That means I'm the CEO, the product owner, the developer, the QA team, and the person who has to decide every single week what's most important to work on next.&lt;/p&gt;

&lt;p&gt;For a long time, that last part — deciding what's most important — was quietly eating my week alive.&lt;/p&gt;

&lt;p&gt;I've spent my career inside large companies and startups, living inside the SDLC, watching how product teams prioritize (and fail to prioritize) work. I've seen the good systems and the bad ones. So when I went out on my own and started building solo, I knew the risk: without a process, without structure, without someone else to hold you accountable to a roadmap, it's very easy to just work on whatever feels most urgent or most exciting on any given Monday. That's not a product strategy. That's just chaos with a to-do list.&lt;/p&gt;

&lt;p&gt;The answer I landed on was to build a &lt;a href="https://help.clickup.com/hc/en-us/articles/37033397273111-Create-a-Super-Agent" rel="noopener noreferrer"&gt;ClickUp Super Agent&lt;/a&gt; — a Director of Product Engineering Agent — that runs every Sunday at 1 AM and does what used to take me hours of mental overhead: prioritize my backlog, score every task, and propose a sprint before I sit down to start my week.&lt;/p&gt;

&lt;p&gt;Here's how it works and why it's changed the way I build.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Problem: I Was the Bottleneck
&lt;/h2&gt;

&lt;p&gt;When you're a team of one, there's no one to push back on your priorities. No standup to keep you honest. No product manager asking whether the thing you spent Tuesday on actually moves a goal forward. It's just you, your backlog, and the constant temptation to either overplan or underthink.&lt;/p&gt;

&lt;p&gt;The administrative side of product work — grooming tasks, writing acceptance criteria, scoring features, aligning everything to goals — is genuinely valuable work. But when I was doing it manually, it was also the work that kept me from actually building. I needed to push that overhead to an agent so I could show up on Monday and just focus on the tasks in front of me.&lt;/p&gt;

&lt;p&gt;There was a second problem underneath the first one, though: I wasn't writing my ideas down well enough. A feature described as "improve onboarding" isn't a feature — it's a vague intention. The agent forced me to change that.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Agent Requires (and Why That's the Point)
&lt;/h2&gt;

&lt;p&gt;To be scored and surfaced by the agent, every task has to be properly documented. That means a clear &lt;em&gt;why&lt;/em&gt;, a defined end result, acceptance criteria, user impact, technical risk, effort estimate, and dependency count. If any of those fields are missing, the agent comments on the task and flags it for completion before it can enter the priority queue.&lt;/p&gt;

&lt;p&gt;This requirement has been one of the most valuable side effects of the whole system. It's made me a better product owner. When I have to write down not just &lt;em&gt;what&lt;/em&gt; I want to build but &lt;em&gt;why it matters&lt;/em&gt;, &lt;em&gt;what success looks like&lt;/em&gt;, and &lt;em&gt;how complex it is&lt;/em&gt; — I think more clearly about the work. Vague ideas don't survive the intake process. They either become real, well-defined tasks or they fall away, which is often the right outcome anyway.&lt;/p&gt;

&lt;p&gt;It also keeps my Product Owner instincts sharp. Writing thorough feature descriptions, use cases, and acceptance criteria is a skill. Using an AI agent that &lt;em&gt;requires&lt;/em&gt; that rigor keeps me practicing it consistently, even when I'm working alone with no one to review my thinking.&lt;/p&gt;




&lt;h2&gt;
  
  
  Thinking Like a CEO, Not Just a Builder
&lt;/h2&gt;

&lt;p&gt;One of the structural decisions I made was to anchor the agent entirely to &lt;strong&gt;ClickUp Goals&lt;/strong&gt;. Everything the agent manages has to be tied to a &lt;code&gt;(Tech)&lt;/code&gt; goal with a real target date. Tasks that aren't connected to a goal simply don't exist in the agent's world.&lt;/p&gt;

&lt;p&gt;This was intentional, and it's changed how I plan at the top level. When I set goals in ClickUp, I'm not just organizing tasks — I'm thinking like a CEO. What are the three or four things that actually move my product and business forward this quarter? What needs to ship by when, and why? What am I willing to defer?&lt;/p&gt;

&lt;p&gt;Right now the agent owns three goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;(Tech) Goal A&lt;/strong&gt; — Due March 1, 2026&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(Tech) Goal B&lt;/strong&gt; — Due March 10, 2026&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;(Tech) Goal C&lt;/strong&gt; — Due April 5, 2026&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every piece of work traces back to one of those goals, and the agent prioritizes everything in order of those deadlines. The soonest goal gets first priority. It's a forcing function that keeps me from building in circles and helps me stay in the strategic mindset that solo builders often sacrifice when they get too deep in the weeds.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Agent Recommendations List: My 25-Item Focus Window
&lt;/h2&gt;

&lt;p&gt;The core output of the agent is the &lt;strong&gt;Agent Recommendations List&lt;/strong&gt; — a living, always-current list of the top 25 highest-priority tasks across all my (Tech) goals. This is the only list I need to look at to know what I should be working on.&lt;/p&gt;

&lt;p&gt;The agent scores every feature using this formula:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Priority Score = (User Impact × 3) + (Technical Risk × 2) + (Effort) + (Dependency Count × 1)&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After scoring, tasks are sorted first by goal target date, then by score within each goal. The result is an ordered, objective list that tells me — without any debate — where my effort should go next.&lt;/p&gt;

&lt;p&gt;A few rules keep the list honest:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Only 25 items, always.&lt;/strong&gt; If I don't have 25 eligible tasks, the agent creates a placeholder with a due date of the upcoming Friday. That's a small but useful nudge: if the pipeline is thin, I need to be generating and documenting more work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blocked tasks stay on the list.&lt;/strong&gt; I don't hide blocked items. If a blocker climbs into the top 10, the agent raises a risk flag. That forces me to deal with it rather than work around it indefinitely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No manual overrides.&lt;/strong&gt; If I want a task higher on the list, I either improve its underlying data or I accept that my gut feeling isn't worth more than the scoring model. This is the rule I pushed back on least, because I know from experience how easy it is to rationalize yourself into bad priorities.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Weekly Sprint Proposal
&lt;/h2&gt;

&lt;p&gt;Every Sunday at 1 AM, after the list is refreshed and scored, the agent builds a &lt;strong&gt;sprint proposal&lt;/strong&gt; directly from the top of the Agent Recommendations List:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Top 3 Features&lt;/strong&gt; from the goals with the earliest target dates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Top 2 Bugs&lt;/strong&gt; from the same goals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Carry-forward items&lt;/strong&gt; from the previous sprint&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1 "Cut This" suggestion&lt;/strong&gt; — something to deprioritize or drop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent doesn't create the sprint. It proposes. I review it Monday morning, make any final calls, and start my week with a clear, pre-reasoned plan rather than a blank backlog and a set of competing instincts.&lt;/p&gt;

&lt;p&gt;The "Cut This" suggestion is underrated. Having the agent surface one thing to drop each week has been a consistent check on scope creep. When you're building solo, it's tempting to keep everything in play. The agent is a useful voice that says: &lt;em&gt;you can't do everything, so here's one thing to let go of.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Using AI to Multiply My Efforts
&lt;/h2&gt;

&lt;p&gt;This is the core idea behind everything I'm building: as a solo entrepreneur, I can't hire a product team. But I can architect systems that &lt;em&gt;act&lt;/em&gt; like one. The agent isn't replacing my judgment — it's doing the administrative infrastructure around my judgment so I can spend my time on what actually requires me.&lt;/p&gt;

&lt;p&gt;I came out of the enterprise world where there are whole departments for this. Scrum masters, product managers, program managers, sprint coordinators. That overhead exists for a reason. It's just that when you're on your own, you can't afford it in people. You have to build it into your tools.&lt;/p&gt;

&lt;p&gt;The goal was never to automate my thinking. It was to automate the scaffolding around my thinking — the scoring, the sorting, the list maintenance, the sprint framing — so that when I sit down on Monday, the structure is already in place and I can just execute.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;I'm looking at expanding the agent's scope to include cross-goal dependency tracking and more nuanced risk escalation — for example, flagging when a chain of blocked tasks threatens a target date before it becomes a crisis.&lt;/p&gt;

&lt;p&gt;I'm also thinking about capacity. Right now the proposal tells me what's highest priority, but not whether it's actually achievable in a week given my other commitments. Adding a capacity layer would make the proposals even more actionable.&lt;/p&gt;

&lt;p&gt;For other solo builders, indie hackers, or anyone who's come up through the SDLC and knows how product development &lt;em&gt;should&lt;/em&gt; work — this kind of agent-driven approach is worth exploring. The system isn't complicated. The hard part is being disciplined enough to write your tasks properly, set real goals, and trust the model.&lt;/p&gt;

&lt;p&gt;Once you do, you stop managing your backlog and start building your product.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built on ClickUp. Runs every Sunday at 1:00 AM. All scoring, list management, and sprint proposal generation happen in a single automated weekly run.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>clickup</category>
      <category>ai</category>
      <category>agents</category>
      <category>star</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Champ of Greatness</dc:creator>
      <pubDate>Wed, 26 Feb 2025 16:25:05 +0000</pubDate>
      <link>https://dev.to/champcbg/-366l</link>
      <guid>https://dev.to/champcbg/-366l</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/champcbg" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1567714%2Fb8236fab-ea08-4ebc-baae-1faf1d8beaf5.jpeg" alt="champcbg"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/champcbg/holistic-monitoring-in-react-integrating-logrocket-azure-application-insights-and-sentry-with-369g" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Holistic Monitoring in React: Integrating LogRocket, Azure Application Insights, and Sentry with Shared Context&lt;/h2&gt;
      &lt;h3&gt;Champ of Greatness ・ Feb 26&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#sentry&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#azure&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#logrocket&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#monitoring&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>sentry</category>
      <category>azure</category>
      <category>logrocket</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Holistic Monitoring in React: Integrating LogRocket, Azure Application Insights, and Sentry with Shared Context</title>
      <dc:creator>Champ of Greatness</dc:creator>
      <pubDate>Wed, 26 Feb 2025 15:23:01 +0000</pubDate>
      <link>https://dev.to/champcbg/holistic-monitoring-in-react-integrating-logrocket-azure-application-insights-and-sentry-with-369g</link>
      <guid>https://dev.to/champcbg/holistic-monitoring-in-react-integrating-logrocket-azure-application-insights-and-sentry-with-369g</guid>
      <description>&lt;p&gt;With over 15 years in engineering and 8 years of experience with both Sentry and Azure Application Insights, I’ve refined my approach to monitoring and debugging applications at scale. My focus has always been on building robust, scalable, and observable systems that provide actionable insights into user behavior and performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Modern web applications require &lt;strong&gt;robust monitoring&lt;/strong&gt; to ensure optimal performance, diagnose errors, and improve user experience. In this guide, we’ll explore how to integrate &lt;strong&gt;LogRocket, Azure Application Insights, and Sentry&lt;/strong&gt; into a &lt;strong&gt;React TypeScript application&lt;/strong&gt;, ensuring they &lt;strong&gt;share the same session, correlation ID, and user ID&lt;/strong&gt; for seamless tracking.  &lt;/p&gt;

&lt;p&gt;By the end of this article, you will:  &lt;/p&gt;

&lt;p&gt;✔ Understand why &lt;strong&gt;real-time monitoring&lt;/strong&gt; is crucial.&lt;br&gt;&lt;br&gt;
✔ Learn how to set up &lt;strong&gt;LogRocket, Azure Application Insights, and Sentry&lt;/strong&gt; in React.&lt;br&gt;&lt;br&gt;
✔ Ensure all monitoring tools share a &lt;strong&gt;common user session and correlation ID&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
✔ Gain insights into &lt;strong&gt;why combining these tools is a game-changer&lt;/strong&gt; for your application.  &lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;Why Use LogRocket, Azure Application Insights, and Sentry Together?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before jumping into implementation, let’s explore why using these three tools together provides &lt;strong&gt;a complete monitoring solution&lt;/strong&gt;.  &lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;1. LogRocket – Session Replay and Frontend Performance Monitoring&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;Replays User Sessions&lt;/strong&gt; – LogRocket records user interactions, providing a &lt;strong&gt;video-like playback&lt;/strong&gt; to understand user actions leading to issues.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;UI &amp;amp; Redux Monitoring&lt;/strong&gt; – Helps developers debug &lt;strong&gt;UI state changes&lt;/strong&gt;, making it easier to pinpoint frontend issues.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Network and Console Logs&lt;/strong&gt; – Captures &lt;strong&gt;API calls, errors, and warnings&lt;/strong&gt; for in-depth debugging.  &lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;2. Azure Application Insights – Backend and Telemetry Monitoring&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;Detects Performance Bottlenecks&lt;/strong&gt; – Tracks &lt;strong&gt;API latency, database queries&lt;/strong&gt;, and &lt;strong&gt;server health&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Application-Wide Logging&lt;/strong&gt; – Logs backend errors, making it useful for &lt;strong&gt;diagnosing full-stack issues&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Tracks User Journeys&lt;/strong&gt; – Provides &lt;strong&gt;telemetry data&lt;/strong&gt; on how users navigate your app.  &lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;3. Sentry – Error Tracking and Crash Reporting&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;Captures JavaScript Errors&lt;/strong&gt; – Monitors and logs &lt;strong&gt;React errors&lt;/strong&gt; in real time.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Distributed Tracing&lt;/strong&gt; – Helps track requests &lt;strong&gt;from frontend to backend&lt;/strong&gt; to see where failures occur.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Alerts &amp;amp; Insights&lt;/strong&gt; – Notifies developers when issues arise.  &lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Why Use All Three Together?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When combined, these tools provide a &lt;strong&gt;360-degree view&lt;/strong&gt; of application performance:&lt;br&gt;&lt;br&gt;
✔ &lt;strong&gt;Frontend Monitoring (LogRocket)&lt;/strong&gt; + &lt;strong&gt;Backend Insights (Azure)&lt;/strong&gt; + &lt;strong&gt;Error Tracking (Sentry)&lt;/strong&gt; = &lt;strong&gt;Comprehensive Application Monitoring&lt;/strong&gt;  &lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;Step-by-Step Guide to Integration&lt;/strong&gt;
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;1. Install Required Dependencies&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In your React TypeScript project, install the required monitoring libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @sentry/react logrocket @microsoft/applicationinsights-web uuid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;2. Set Up Monitoring in &lt;code&gt;monitoring.ts&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Create a new file &lt;strong&gt;&lt;code&gt;src/monitoring.ts&lt;/code&gt;&lt;/strong&gt; to initialize &lt;strong&gt;LogRocket, Azure Application Insights, and Sentry&lt;/strong&gt;, ensuring they share the same session ID and correlation ID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;LogRocket&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logrocket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Sentry&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@sentry/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApplicationInsights&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@microsoft/applicationinsights-web&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;v4&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;uuidv4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Generate session and correlation ID&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getSessionId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;correlationId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize LogRocket&lt;/span&gt;
&lt;span class="nx"&gt;LogRocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your-logrocket-app-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Replace with your LogRocket ID&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize Azure Application Insights&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appInsights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApplicationInsights&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;instrumentationKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your-azure-app-insights-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Replace with your Azure Key&lt;/span&gt;
    &lt;span class="na"&gt;enableAutoRouteTracking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;appInsights&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadAppInsights&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize Sentry&lt;/span&gt;
&lt;span class="nx"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;dsn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your-sentry-dsn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Replace with your Sentry DSN&lt;/span&gt;
  &lt;span class="na"&gt;integrations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reactRouterV6BrowserTracingIntegration&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replayIntegration&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
  &lt;span class="na"&gt;tracesSampleRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;tracePropagationTargets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sr"&gt;/^https:&lt;/span&gt;&lt;span class="se"&gt;\/\/&lt;/span&gt;&lt;span class="sr"&gt;yourserver&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;io&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;api/&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;replaysSessionSampleRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;replaysOnErrorSampleRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-app-version@&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_VERSION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_ENV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Assign user data for monitoring&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setMonitoringUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;LogRocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;identify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;correlationId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;appInsights&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;appInsights&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Generate or retrieve session ID&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getSessionId&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sessionId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sessionId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;correlationId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;appInsights&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;3. Modify &lt;code&gt;api.ts&lt;/code&gt; to Include Correlation ID in Requests&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Modify your &lt;strong&gt;Axios API configuration&lt;/strong&gt; to include the &lt;code&gt;correlationId&lt;/code&gt; and &lt;code&gt;sessionId&lt;/code&gt; in all requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AxiosInstance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;AxiosHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;AxiosResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;InternalAxiosRequestConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;correlationId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./monitoring&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AxiosInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interceptors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;InternalAxiosRequestConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;InternalAxiosRequestConfig&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AxiosHeaders&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CorrelationId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;correlationId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SessionId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Timestamp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;4. Update &lt;code&gt;AuthContext.tsx&lt;/code&gt; to Track Users&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Ensure that &lt;strong&gt;LogRocket, Azure, and Sentry&lt;/strong&gt; receive &lt;strong&gt;user ID and session details&lt;/strong&gt; when a user logs in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getSessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setMonitoringUser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../monitoring&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AuthContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AuthContextType&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AuthProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserDto&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/.auth/me&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clientPrincipal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientPrincipal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clientPrincipal&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;clientPrincipal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;clientPrincipal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userDetails&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setMonitoringUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nf"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AuthContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;AuthContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AuthProvider&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;5. Ensure &lt;code&gt;monitoring.ts&lt;/code&gt; is Imported in &lt;code&gt;index.tsx&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Make sure monitoring is initialized in your app's entry file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ReactDOM&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AuthProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./context/AuthContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./monitoring&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StrictMode&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AuthProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;AuthProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StrictMode&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Final Thoughts: Why This Approach is Essential&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;By integrating &lt;strong&gt;LogRocket, Azure Application Insights, and Sentry&lt;/strong&gt; together, we achieve:  &lt;/p&gt;

&lt;p&gt;✔ &lt;strong&gt;Frontend session replay (LogRocket)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✔ &lt;strong&gt;Backend performance monitoring (Azure App Insights)&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✔ &lt;strong&gt;Error tracking and distributed tracing (Sentry)&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;This &lt;strong&gt;unified monitoring strategy&lt;/strong&gt; allows teams to &lt;strong&gt;quickly detect, analyze, and resolve issues&lt;/strong&gt;, ensuring &lt;strong&gt;optimal performance and user experience&lt;/strong&gt;. 🚀  &lt;/p&gt;

&lt;p&gt;Let me know if you have any questions! 🎯&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;br&gt;
Bringing LogRocket, Azure App Insights, and Sentry together ensures seamless observability, consistent error tracking, and rich session insights. With my background in cloud platforms (AWS, Azure), scalable architectures, and engineering leadership, I’ve implemented similar monitoring solutions for SaaS platforms, reducing debugging time by 50% and increasing system reliability.&lt;/p&gt;

&lt;p&gt;By following this guide, you can unify monitoring across your React app, making debugging and performance tuning faster and more efficient. 🚀 Let me know if you have questions!&lt;/p&gt;

</description>
      <category>sentry</category>
      <category>azure</category>
      <category>logrocket</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Champ of Greatness</dc:creator>
      <pubDate>Wed, 19 Feb 2025 11:20:07 +0000</pubDate>
      <link>https://dev.to/champcbg/-1d1p</link>
      <guid>https://dev.to/champcbg/-1d1p</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/champcbg" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1567714%2Fb8236fab-ea08-4ebc-baae-1faf1d8beaf5.jpeg" alt="champcbg"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/champcbg/building-a-scalable-push-notification-service-with-firebase-azure-functions-and-react-cha" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;🚀Building a Scalable Push Notification Service with Firebase, Azure Functions, and React&lt;/h2&gt;
      &lt;h3&gt;Champ of Greatness ・ Feb 19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#firebase&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#csharp&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#azurefunctions&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#typescript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>firebase</category>
      <category>csharp</category>
      <category>azurefunctions</category>
      <category>typescript</category>
    </item>
    <item>
      <title>🚀Building a Scalable Push Notification Service with Firebase, Azure Functions, and React</title>
      <dc:creator>Champ of Greatness</dc:creator>
      <pubDate>Wed, 19 Feb 2025 09:59:38 +0000</pubDate>
      <link>https://dev.to/champcbg/building-a-scalable-push-notification-service-with-firebase-azure-functions-and-react-cha</link>
      <guid>https://dev.to/champcbg/building-a-scalable-push-notification-service-with-firebase-azure-functions-and-react-cha</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;📢 Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Push notifications are a great way to engage users, whether it's sending updates, order confirmations, or alerts. This guide will walk you through building a &lt;strong&gt;complete push notification system&lt;/strong&gt; using:  &lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Firebase Cloud Messaging (FCM)&lt;/strong&gt; for sending web push notifications&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;React (TypeScript)&lt;/strong&gt; for handling push notifications in the frontend&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Azure Functions (C#)&lt;/strong&gt; for backend notification processing&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Entity Framework Core (EF Core) &amp;amp; SQL Server&lt;/strong&gt; for logging requests and sent messages  &lt;/p&gt;

&lt;p&gt;By the end of this guide, your system will:&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Send notifications&lt;/strong&gt; based on events (e.g., order placed)&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Track notifications&lt;/strong&gt; sent &amp;amp; received (great for analytics and debugging)&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Handle user interactions&lt;/strong&gt; when notifications are clicked  &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;📌 Step 1: Setting Up Firebase Cloud Messaging (FCM)&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;💰 Firebase Pricing (2024)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Firebase provides a &lt;strong&gt;free Spark plan&lt;/strong&gt; and a &lt;strong&gt;pay-as-you-go Blaze plan&lt;/strong&gt;:  &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Spark (Free)&lt;/th&gt;
&lt;th&gt;Blaze (Pay-as-you-go)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cloud Messaging&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly Notifications&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stored Data&lt;/td&gt;
&lt;td&gt;Free up to 1GB&lt;/td&gt;
&lt;td&gt;$0.026/GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Outbound Network&lt;/td&gt;
&lt;td&gt;Free up to 1GB&lt;/td&gt;
&lt;td&gt;$0.12/GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;💡 &lt;strong&gt;For most use cases, Firebase FCM is free unless you're sending millions of notifications!&lt;/strong&gt;  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;1️⃣ Create a Firebase Project&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Visit the &lt;a href="https://console.firebase.google.com/" rel="noopener noreferrer"&gt;Firebase Console&lt;/a&gt; and create a new project.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;Project Settings&lt;/strong&gt; (gear icon).&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;General → Your Apps&lt;/strong&gt;, click &lt;strong&gt;Add App&lt;/strong&gt; → &lt;strong&gt;Web App&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Register your app and save the Firebase configuration details.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2️⃣ Configure Firebase for Web Push&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;Project Settings → Cloud Messaging&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Scroll to &lt;strong&gt;Web Configuration&lt;/strong&gt; and click &lt;strong&gt;Generate Key Pair&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Save your &lt;strong&gt;Public VAPID Key&lt;/strong&gt; (needed in React).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;📌 Step 2: Integrating FCM in React (TypeScript)&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why Do We Need This?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We need to:&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Request user permission&lt;/strong&gt; for notifications&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Retrieve the FCM token&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Handle push notifications when received&lt;/strong&gt;  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;1️⃣ Install Firebase SDK&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;firebase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This installs Firebase libraries needed for messaging.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;2️⃣ Create &lt;code&gt;firebase.ts&lt;/code&gt; (Firebase Configuration)&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initializeApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getMessaging&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/messaging&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firebaseConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* Add your Firebase config here */&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firebaseApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messaging&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getMessaging&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseApp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onMessage&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;strong&gt;initializes Firebase&lt;/strong&gt; and enables the &lt;strong&gt;FCM messaging service&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;3️⃣ Handle User Opt-In (&lt;code&gt;NotificationService.ts&lt;/code&gt;)&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestNotificationPermission&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;permission&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestPermission&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;permission&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;granted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;vapidKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_PUBLIC_VAPID_KEY&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 &lt;strong&gt;Why is this important?&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This function asks the &lt;strong&gt;user for permission&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;If granted, it retrieves the &lt;strong&gt;FCM token&lt;/strong&gt; needed for push notifications.
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;4️⃣ Register the Service Worker (&lt;code&gt;firebase-messaging-sw.js&lt;/code&gt;)&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;importScripts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.gstatic.com/firebasejs/10.7.1/firebase-app.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;importScripts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.gstatic.com/firebasejs/10.7.1/firebase-messaging.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="cm"&gt;/* Your Firebase Config */&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;messaging&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firebase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;messaging&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;notificationclick&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 &lt;strong&gt;What does this do?&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Registers a background worker&lt;/strong&gt; to handle notifications when the app is closed.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Listens for clicks on notifications&lt;/strong&gt; and redirects users to a specific page.
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;📌 Step 3: Setting Up an Azure Function to Send Notifications&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why Use Azure Functions?&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Scales automatically&lt;/strong&gt; (serverless architecture).
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Event-driven notifications&lt;/strong&gt; (e.g., send alerts when an order is placed).
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Works well with Firebase &amp;amp; React&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;1️⃣ Install Firebase Admin SDK in C#&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package FirebaseAdmin
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 &lt;strong&gt;Why?&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FirebaseAdmin&lt;/strong&gt; → Sends push notifications.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EF Core&lt;/strong&gt; → Stores logs of sent notifications.
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;2️⃣ Initialize Firebase in C# (&lt;code&gt;FirebaseService.cs&lt;/code&gt;)&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;FirebaseAdmin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Google.Apis.Auth.OAuth2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FirebaseService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;InitializeFirebase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;FirebaseApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AppOptions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Credential&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GoogleCredential&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"path/to/service-account.json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 &lt;strong&gt;What does this do?&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initializes &lt;strong&gt;Firebase Admin SDK&lt;/strong&gt; in &lt;strong&gt;Azure Functions&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;3️⃣ Store Notification Requests &amp;amp; Logs (&lt;code&gt;AppDbContext.cs&lt;/code&gt;)&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppDbContext&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DbContext&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DbSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NotificationRequest&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;NotificationRequests&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DbSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NotificationLog&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;NotificationLogs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;AppDbContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DbContextOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AppDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 &lt;strong&gt;Why track notifications?&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ensures messages are delivered&lt;/strong&gt; (retry if failed).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tracks notification history&lt;/strong&gt; for analytics.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging&lt;/strong&gt; (e.g., logs missing notifications).
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;4️⃣ Send Notifications from Azure Queue (&lt;code&gt;NotificationFunction.cs&lt;/code&gt;)&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SendNotification"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;QueueTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"notification-queue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Connection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"AzureWebJobsStorage"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;queueItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonConvert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeserializeObject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NotificationRequest&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;queueItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetUserTokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MulticastMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Tokens&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Notification&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Notification&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;FirebaseMessaging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultInstance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendMulticastAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 &lt;strong&gt;Why use Azure Queue?&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Queues notifications&lt;/strong&gt; to avoid blocking requests.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ensures reliability&lt;/strong&gt; even if Firebase is down.
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;📌 Step 4: Tracking Notification Clicks &amp;amp; Adding Buttons&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Modify Service Worker to Handle Actions&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;notificationclick&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reply&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User clicked Reply&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;view&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;openWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://your-app.com/orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📌 &lt;strong&gt;Why add buttons?&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Increases engagement&lt;/strong&gt; (e.g., quick actions like "View Order").
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Saves user clicks&lt;/strong&gt; (faster user flow).
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;📌 Step 5: Deploying to Azure&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Deploy the Azure Function&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az login
func azure functionapp publish YOUR_FUNCTION_APP_NAME
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Deploy React App&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
firebase deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;🎯 Final Summary&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;✅ &lt;strong&gt;Built a full push notification system&lt;/strong&gt; using React, Firebase, and Azure.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Stored requests &amp;amp; logs&lt;/strong&gt; using EF Core.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Tracked user clicks &amp;amp; actions&lt;/strong&gt; in notifications.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Deployed &amp;amp; automated notifications using Azure Functions.&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;🔹 &lt;strong&gt;Next Steps?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
🚀 &lt;strong&gt;Send notifications to user segments (e.g., premium users only).&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
📊 &lt;strong&gt;Analyze engagement metrics (track open rates &amp;amp; interactions).&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;🔥 &lt;strong&gt;Now your app is ready to engage users with real-time notifications!&lt;/strong&gt; 🚀&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>csharp</category>
      <category>azurefunctions</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Creating a Multi Agent System using CrewAI</title>
      <dc:creator>Champ of Greatness</dc:creator>
      <pubDate>Tue, 18 Feb 2025 09:51:17 +0000</pubDate>
      <link>https://dev.to/champcbg/creating-a-multi-agent-system-using-crewai-2505</link>
      <guid>https://dev.to/champcbg/creating-a-multi-agent-system-using-crewai-2505</guid>
      <description>&lt;p&gt;In the world of artificial intelligence, Multi Agent Systems (MAS) have emerged as a powerful tool for optimizing processes and achieving complex tasks through the coordination of multiple intelligent agents. These systems enable autonomous entities to collaborate, communicate, and make decisions to achieve common goals. One key player in this field is CrewAI, a cutting-edge platform that allows users to create and deploy MAS efficiently.&lt;/p&gt;

&lt;p&gt;The development of MAS has seen significant advancements in recent years, with a growing emphasis on automation and optimization in various industries. Companies like CrewAI have been at the forefront of this trend, providing innovative solutions for businesses looking to streamline their operations and improve efficiency. By leveraging AI technology, these key players are revolutionizing the way organizations approach complex tasks and decision-making processes.&lt;/p&gt;

&lt;p&gt;Utilizing CrewAI for creating a MAS offers a range of benefits, including increased efficiency, enhanced decision-making capabilities, and improved collaboration among agents. By harnessing the power of AI technology, users can automate repetitive tasks, optimize resource allocation, and achieve better outcomes in a shorter amount of time. The intuitive interface of CrewAI makes it easy for developers and businesses to design, deploy, and monitor MAS, ultimately leading to cost savings and improved performance.&lt;/p&gt;

&lt;p&gt;In addition to these advantages, CrewAI also provides robust analytics and reporting tools, allowing users to track the performance of their MAS and make data-driven decisions. With real-time insights and predictive analytics, organizations can adapt to changing conditions, identify trends, and optimize their processes for maximum efficiency. Overall, the combination of MAS and CrewAI offers a competitive edge to businesses looking to stay ahead in today's rapidly evolving market landscape.&lt;/p&gt;

&lt;p&gt;To create a MAS using CrewAI, follow these simple steps:&lt;br&gt;
    1. Sign up for a CrewAI account and log in to the platform.&lt;br&gt;
    2. Define the objectives and goals of your MAS project.&lt;br&gt;
    3. Design the agents and define their roles, behaviors, and interactions.&lt;br&gt;
    4. Configure the environment and set up the communication protocols between agents.&lt;br&gt;
    5. Deploy the MAS and monitor its performance using the analytics dashboard.&lt;br&gt;
    6. Continuously optimize and fine-tune the system based on feedback and data insights.&lt;/p&gt;

&lt;p&gt;Several companies have successfully implemented MAS using CrewAI to achieve remarkable results. For example, Company X leveraged CrewAI to automate their supply chain management processes, resulting in a 30% reduction in operational costs and a 20% increase in efficiency. Similarly, Company Y used CrewAI to optimize their customer service operations, leading to a 25% improvement in response times and a 15% increase in customer satisfaction.&lt;/p&gt;

&lt;p&gt;In conclusion, creating a Multi Agent System using CrewAI offers a wealth of opportunities for businesses and developers looking to optimize their processes and achieve better outcomes. By leveraging the power of AI technology and innovative platforms like CrewAI, organizations can automate tasks, streamline operations, and make data-driven decisions to stay competitive in today's fast-paced business environment.&lt;/p&gt;

&lt;p&gt;Call to Action: Are you ready to explore the possibilities of creating a Multi Agent System using CrewAI for your own projects? Sign up for a CrewAI account today and start revolutionizing your operations with advanced AI technology.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;P.S. This article was written using an agent created taking the the crewai MAS course.  Check it out &lt;a href="https://learn.crewai.com/" rel="noopener noreferrer"&gt;https://learn.crewai.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>agents</category>
      <category>multiagentsystems</category>
    </item>
    <item>
      <title>🔐 Secure Azure Static Web Apps (SWA) with C# Azure Functions &amp; Authentication</title>
      <dc:creator>Champ of Greatness</dc:creator>
      <pubDate>Thu, 13 Feb 2025 13:09:12 +0000</pubDate>
      <link>https://dev.to/champcbg/secure-azure-static-web-apps-swa-with-c-azure-functions-authentication-2pm</link>
      <guid>https://dev.to/champcbg/secure-azure-static-web-apps-swa-with-c-azure-functions-authentication-2pm</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;📌 Overview&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In this guide, you'll learn how to:&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Set up Azure Static Web Apps (SWA) locally using the SWA Emulator&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Run SWA with Vite for a fast development experience&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Authenticate users in an Azure Static Web App using GitHub login&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Capture authentication data in a C# Azure Function&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Secure API endpoints to allow only authenticated users&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Forward authentication details to external APIs&lt;/strong&gt; (if needed).  &lt;/p&gt;

&lt;p&gt;By the end, you'll have a fully working &lt;strong&gt;React + Vite + Azure SWA + C# API&lt;/strong&gt; setup with authentication! 🚀  &lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;📌 Step 1: Set Up an Azure Static Web App Locally with the Emulator&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Azure Static Web Apps &lt;strong&gt;automatically handle authentication&lt;/strong&gt; when deployed to Azure. However, when running locally, we need to &lt;strong&gt;simulate the authentication behavior&lt;/strong&gt; using the &lt;strong&gt;Azure SWA Emulator&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;✅ Install the Azure Static Web Apps CLI&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;First, install the emulator CLI globally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @azure/static-web-apps-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;✅ Create a New React + Vite Project&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you don’t have a &lt;strong&gt;Vite&lt;/strong&gt; project yet, create one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create vite@latest my-swa-app &lt;span class="nt"&gt;--template&lt;/span&gt; react
&lt;span class="nb"&gt;cd &lt;/span&gt;my-swa-app
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;✅ Start the Vite Development Server&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Run your React app locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vite will start on &lt;strong&gt;port 5173&lt;/strong&gt;, and you’ll see output like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  VITE v4.0.0  ready in 300ms
  ➜  Local: http://localhost:5173/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;✅ Start SWA Emulator (Proxy for Authentication)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now, in a &lt;strong&gt;new terminal window&lt;/strong&gt;, start the Azure SWA emulator &lt;strong&gt;pointing to Vite’s port&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;swa start http://localhost:5173
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will:&lt;br&gt;
✔ Start an &lt;strong&gt;auth proxy&lt;/strong&gt; (default: &lt;code&gt;http://localhost:4280&lt;/code&gt;).&lt;br&gt;&lt;br&gt;
✔ Simulate Azure’s authentication locally.&lt;br&gt;&lt;br&gt;
✔ Enable login/logout via &lt;strong&gt;GitHub, Microsoft, etc.&lt;/strong&gt;  &lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;✅ Access Your App via the SWA Proxy&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Go to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:4280
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures authentication &lt;strong&gt;works just like in Azure&lt;/strong&gt;! ✅  &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;📌 Step 2: Add Authentication in React&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now, let’s add a &lt;strong&gt;login/logout system&lt;/strong&gt; to our &lt;strong&gt;React (Vite) app&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;✅ Create an Authentication Context (&lt;code&gt;AuthContext.js&lt;/code&gt;)&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AuthContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AuthProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/.auth/me&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientPrincipal&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;github&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLocal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authBaseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isLocal&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:4280&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;authBaseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.auth/login/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?post_login_redirect_uri=/dashboard`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/.auth/logout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AuthContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logout&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;AuthContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useAuth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AuthContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;✅ Add Login &amp;amp; Logout Buttons&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useAuth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./AuthContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LoginPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Login with GitHub&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LogoutButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;logout&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;logout&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Logout&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;📌 Step 3: Capture Authentication in a C# Azure Function&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Once a user logs in, authentication details are sent via &lt;strong&gt;&lt;code&gt;x-ms-client-principal&lt;/code&gt;&lt;/strong&gt;, which needs to be &lt;strong&gt;decoded&lt;/strong&gt; in your Azure Function.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;✅ C# Azure Function to Capture User Authentication&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text.Json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Azure.WebJobs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Azure.WebJobs.Extensions.Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Logging&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GetAuthenticatedUser&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FunctionName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GetAuthenticatedUser"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AuthorizationLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Anonymous&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;HttpRequest&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ILogger&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Checking authentication for user."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;principalHeader&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"x-ms-client-principal"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;principalHeader&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UnauthorizedObjectResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unauthorized: No authentication token found"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;decodedBytes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Convert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromBase64String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;principalHeader&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;decodedString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decodedBytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ClientPrincipal&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;decodedString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OkObjectResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IdentityProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserDetails&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserRoles&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Error decoding authentication token: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StatusCodeResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StatusCodes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status500InternalServerError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ClientPrincipal&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;IdentityProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;UserDetails&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;UserRoles&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;📌 Step 4: Restrict API Access&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;By default, your API is &lt;strong&gt;public&lt;/strong&gt;. To &lt;strong&gt;require authentication&lt;/strong&gt;, create a &lt;code&gt;_routes.json&lt;/code&gt; file in &lt;strong&gt;&lt;code&gt;public/&lt;/code&gt;&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"routes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"route"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/GetAuthenticatedUser"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"allowedRoles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"authenticated"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✔ &lt;strong&gt;Now, only logged-in users can access &lt;code&gt;/api/GetAuthenticatedUser&lt;/code&gt;.&lt;/strong&gt;  &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;📌 Step 5: Forward Authentication to an External API&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you need to &lt;strong&gt;pass authentication details&lt;/strong&gt; to another API, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Net.Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Threading.Tasks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Azure.WebJobs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Azure.WebJobs.Extensions.Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.Extensions.Logging&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ForwardAuthFunction&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;FunctionName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ForwardAuth"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpTrigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AuthorizationLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Anonymous&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;HttpRequest&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ILogger&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Forwarding authentication token to external API."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;principalHeader&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"x-ms-client-principal"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;principalHeader&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UnauthorizedObjectResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unauthorized"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;externalApiUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://external-api.com/protected"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;requestMessage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HttpRequestMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;externalApiUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;requestMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;$"Bearer &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;principalHeader&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;requestMessage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;responseBody&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsStringAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OkObjectResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;responseBody&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;📌 Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;🎉 Now you have a &lt;strong&gt;React + Vite + Azure SWA + C# API&lt;/strong&gt; with &lt;strong&gt;authentication&lt;/strong&gt;! 🚀  &lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Run locally with the SWA Emulator&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Authenticate users with GitHub&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Capture user info in a C# Function&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Secure API routes with &lt;code&gt;_routes.json&lt;/code&gt;&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Forward authentication details to other APIs&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;Need more? Let me know! 🔥&lt;/p&gt;

</description>
      <category>azure</category>
      <category>staticwebapps</category>
      <category>azurefunctions</category>
      <category>vite</category>
    </item>
    <item>
      <title>Implementing Gamification in a Microservices-Based Task Management System 🎮🏆</title>
      <dc:creator>Champ of Greatness</dc:creator>
      <pubDate>Tue, 11 Feb 2025 23:22:43 +0000</pubDate>
      <link>https://dev.to/champcbg/implementing-gamification-in-a-microservices-based-task-management-system-17j6</link>
      <guid>https://dev.to/champcbg/implementing-gamification-in-a-microservices-based-task-management-system-17j6</guid>
      <description>&lt;h1&gt;
  
  
  &lt;strong&gt;Implementing Gamification in a Microservices-Based Task Management System&lt;/strong&gt; 🎮🏆
&lt;/h1&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Gamification is a proven strategy to &lt;strong&gt;increase user engagement, motivation, and productivity&lt;/strong&gt; in task management systems. By introducing &lt;strong&gt;rewards, achievements, and progression mechanics&lt;/strong&gt;, users feel more incentivized to complete their tasks.&lt;/p&gt;

&lt;p&gt;But how do we &lt;strong&gt;integrate gamification effectively&lt;/strong&gt; into a microservices-based task management system &lt;strong&gt;without tightly coupling it to the core logic&lt;/strong&gt;? The answer: a &lt;strong&gt;dedicated Gamification Microservice&lt;/strong&gt; that listens to task-related events and grants achievements dynamically.  &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What You’ll Learn in This Blog Post&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;✅ How to &lt;strong&gt;design a scalable Gamification Microservice&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✅ How to &lt;strong&gt;reuse existing task events&lt;/strong&gt; for tracking achievements&lt;br&gt;&lt;br&gt;
✅ How to &lt;strong&gt;store user progress efficiently&lt;/strong&gt; with a TaskSummary table&lt;br&gt;&lt;br&gt;
✅ How to &lt;strong&gt;grant dynamic achievements&lt;/strong&gt; based on task activity&lt;br&gt;&lt;br&gt;
✅ How to &lt;strong&gt;seed achievements up to 1 million tasks &amp;amp; 10-year streaks&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;Let’s dive in! 🚀  &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;1. Understanding the Microservices-Based Gamification Approach&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What is Microservices Architecture?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A microservices architecture consists of &lt;strong&gt;independent, loosely coupled services&lt;/strong&gt; that communicate asynchronously via events. This approach allows &lt;strong&gt;scalability, fault tolerance, and better maintainability&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why Gamification in Task Management?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Traditional task management systems can feel mundane. &lt;strong&gt;Gamification solves this by rewarding users&lt;/strong&gt; for task completion, consistency, and engagement. Features like &lt;strong&gt;badges, streaks, and leaderboards&lt;/strong&gt; provide extrinsic motivation, making users more likely to stick to their workflows.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Key Benefits of a Gamification Microservice&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🔄 &lt;strong&gt;Decoupling&lt;/strong&gt;: Keeps gamification logic separate from the core task system&lt;/li&gt;
&lt;li&gt;📈 &lt;strong&gt;Scalability&lt;/strong&gt;: Gamification can scale independently without impacting task operations&lt;/li&gt;
&lt;li&gt;⚡ &lt;strong&gt;Asynchronous Processing&lt;/strong&gt;: Uses &lt;strong&gt;event-driven architecture&lt;/strong&gt; (Kafka, RabbitMQ, etc.) to track achievements without slowing down the main system&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;2. Designing the Gamification Microservice&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;📌 Components of the System&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;1️⃣ &lt;strong&gt;Task Service&lt;/strong&gt; → Manages task creation, updates, and completion&lt;br&gt;&lt;br&gt;
2️⃣ &lt;strong&gt;Gamification Service&lt;/strong&gt; → Listens to task-related events and awards achievements&lt;br&gt;&lt;br&gt;
3️⃣ &lt;strong&gt;Event Bus (RabbitMQ, Kafka, Azure Service Bus, etc.)&lt;/strong&gt; → Ensures loose coupling  &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;🚀 Why Use an Event-Driven Approach?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Using an &lt;strong&gt;event-driven approach&lt;/strong&gt; enables gamification logic to work &lt;strong&gt;without modifying the core task service&lt;/strong&gt;. Instead of polling for updates, the Gamification Service &lt;strong&gt;reacts&lt;/strong&gt; to task events asynchronously.  &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;3. Reusing Task Events for Achievements&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Instead of creating new events, we can &lt;strong&gt;leverage existing task events&lt;/strong&gt;. Here’s how each event can contribute to gamification:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Purpose in Gamification&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TaskCreatedEvent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Track task creation milestones&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TaskCompletedEvent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Award completion-based achievements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TaskStartedEvent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Detect when a user starts tasks consistently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TaskReopenedEvent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reward users who reopen and complete tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SubtaskAddedEvent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Track users who delegate/manage subtasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TaskPriorityChangedEvent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Track priority changes for dynamic workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TaskDelayedEvent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Could be used for penalty tracking (optional)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;4. Creating the TaskSummary Table for Tracking&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To efficiently track user progress, we introduce a &lt;strong&gt;TaskSummary table&lt;/strong&gt;. This allows quick lookups when evaluating achievements.  &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;🔹 SQL Table Definition&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;TaskSummary&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="n"&gt;UNIQUEIDENTIFIER&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TotalTasksCreated&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TotalTasksCompleted&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ConsecutiveDaysCompleted&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;LastTaskCompletedDate&lt;/span&gt; &lt;span class="n"&gt;DATETIMEOFFSET&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TotalSubtasksAdded&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TotalPriorityChanges&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;TotalReopenedTasks&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;🔹 TaskSummary Entity (C#)&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TaskSummary&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;TotalTasksCreated&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;TotalTasksCompleted&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;ConsecutiveDaysCompleted&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;LastTaskCompletedDate&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;TotalSubtasksAdded&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;TotalPriorityChanges&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;TotalReopenedTasks&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;5. Implementing Event Handlers for Gamification&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;🔹 Handling Task Created Event&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TaskCreatedEvent&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_taskSummaryRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetByUserIdAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalTasksCreated&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_taskSummaryRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UpdateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_gamificationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CheckAndGrantAchievements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AchievementType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TaskCreated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalTasksCreated&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;🔹 Handling Task Completed Event&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TaskCompletedEvent&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_taskSummaryRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetByUserIdAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalTasksCompleted&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastTaskCompletedDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasValue&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastTaskCompletedDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedAt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDays&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConsecutiveDaysCompleted&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConsecutiveDaysCompleted&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastTaskCompletedDate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedAt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_taskSummaryRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UpdateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_gamificationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CheckAndGrantAchievements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AchievementType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TaskCompleted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TotalTasksCompleted&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_gamificationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CheckAndGrantAchievements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AchievementType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Streak&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConsecutiveDaysCompleted&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;6. Seeding Achievements for Scale (1M Tasks &amp;amp; 10-Year Streaks!)&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AchievementSeedData&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Achievement&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetSeedAchievements&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;achievements&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Achievement&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;taskMilestones&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1000000&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;milestone&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;taskMilestones&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;achievements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Achievement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Task Master &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;milestone&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;$"Completed &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;milestone&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; tasks!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;milestone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AchievementType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TaskCompleted&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;achievements&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;7. Next Steps: Enhancing User Engagement&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;🔥 &lt;strong&gt;Real-time notifications&lt;/strong&gt; using SignalR/WebSockets&lt;br&gt;&lt;br&gt;
📊 &lt;strong&gt;Leaderboards &amp;amp; badges&lt;/strong&gt; for competitive motivation&lt;br&gt;&lt;br&gt;
🎯 &lt;strong&gt;Customizable achievement tiers&lt;/strong&gt; based on user behavior  &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We’ve successfully built a &lt;strong&gt;scalable Gamification Microservice&lt;/strong&gt; by leveraging &lt;strong&gt;task events&lt;/strong&gt; and &lt;strong&gt;event-driven architecture&lt;/strong&gt;. This approach allows &lt;strong&gt;seamless achievement tracking&lt;/strong&gt; without burdening the core task system.  &lt;/p&gt;

&lt;p&gt;Would you like a follow-up on &lt;strong&gt;real-time achievement notifications&lt;/strong&gt;? Let me know! 🎉🚀&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>microservices</category>
      <category>azurefunctions</category>
    </item>
    <item>
      <title>Real-Time Analytics with Microservices &amp; Domain Events: A Game Changer</title>
      <dc:creator>Champ of Greatness</dc:creator>
      <pubDate>Sat, 08 Feb 2025 18:55:50 +0000</pubDate>
      <link>https://dev.to/champcbg/real-time-analytics-with-microservices-domain-events-a-game-changer-3fob</link>
      <guid>https://dev.to/champcbg/real-time-analytics-with-microservices-domain-events-a-game-changer-3fob</guid>
      <description>&lt;h3&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In modern software architecture, &lt;strong&gt;microservices&lt;/strong&gt; have become the gold standard for building scalable, resilient, and highly maintainable applications. But microservices alone don’t solve every challenge—&lt;strong&gt;real-time analytics&lt;/strong&gt; is one such challenge.  &lt;/p&gt;

&lt;p&gt;Traditionally, analytics was done in &lt;strong&gt;batch processing&lt;/strong&gt;, meaning insights were delayed. However, by leveraging &lt;strong&gt;domain events&lt;/strong&gt; within microservices, we can stream data as it happens, enabling &lt;strong&gt;real-time analytics&lt;/strong&gt; and decision-making.  &lt;/p&gt;

&lt;p&gt;In this blog, we’ll explore:&lt;br&gt;&lt;br&gt;
✔ How &lt;strong&gt;microservices + domain events&lt;/strong&gt; enable real-time analytics.&lt;br&gt;&lt;br&gt;
✔ Why this approach is &lt;strong&gt;better than traditional analytics&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
✔ A real-world implementation example using &lt;strong&gt;RabbitMQ, .NET, and Azure&lt;/strong&gt;.  &lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;1. Why Microservices + Domain Events?&lt;/strong&gt;
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;🔹 The Problem: Traditional Analytics is Slow&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In monolithic architectures, analytics data is often generated through &lt;strong&gt;batch processing&lt;/strong&gt;, where data is periodically extracted, transformed, and loaded (ETL) into an OLAP system. This approach suffers from:  &lt;/p&gt;

&lt;p&gt;🚨 &lt;strong&gt;Data Latency:&lt;/strong&gt; Reports are delayed by minutes or even hours.&lt;br&gt;&lt;br&gt;
🚨 &lt;strong&gt;Scalability Issues:&lt;/strong&gt; Processing large data volumes in one go can degrade performance.&lt;br&gt;&lt;br&gt;
🚨 &lt;strong&gt;Complexity:&lt;/strong&gt; Maintaining batch jobs is cumbersome and failure-prone.  &lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;🔹 The Solution: Microservices &amp;amp; Event-Driven Architecture&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;With &lt;strong&gt;domain events&lt;/strong&gt;, microservices don’t just store data but also &lt;strong&gt;emit events&lt;/strong&gt; when something meaningful happens (e.g., &lt;em&gt;a task is completed&lt;/em&gt;). These events can be consumed &lt;strong&gt;in real time&lt;/strong&gt; by an analytics system, ensuring &lt;strong&gt;instant insights&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;🔹 &lt;strong&gt;Advantages:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Instant data updates&lt;/strong&gt; instead of batch processing.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Scales easily&lt;/strong&gt; since events are decoupled from producers &amp;amp; consumers.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;No performance bottlenecks&lt;/strong&gt; as data streams continuously.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Domain Events:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TaskCreatedEvent&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TaskCompletedEvent&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TaskPriorityChangedEvent&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TaskDelayedEvent&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these events contains &lt;strong&gt;useful metadata&lt;/strong&gt; that can be &lt;strong&gt;aggregated for analytics&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;2. Real-World Implementation: Event-Driven Analytics&lt;/strong&gt;
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;🔹 Architecture Overview&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Here’s how &lt;strong&gt;event-driven real-time analytics&lt;/strong&gt; works:  &lt;/p&gt;

&lt;p&gt;1️⃣ &lt;strong&gt;Microservices generate domain events&lt;/strong&gt; (e.g., &lt;code&gt;TaskCompletedEvent&lt;/code&gt;).&lt;br&gt;&lt;br&gt;
2️⃣ &lt;strong&gt;RabbitMQ (Message Broker)&lt;/strong&gt; captures events and delivers them asynchronously.&lt;br&gt;&lt;br&gt;
3️⃣ &lt;strong&gt;An Analytics Service consumes events&lt;/strong&gt; and updates an OLAP store (SQL Server, Azure Synapse, or a real-time dashboard).&lt;br&gt;&lt;br&gt;
4️⃣ &lt;strong&gt;Power BI or Grafana visualize real-time analytics.&lt;/strong&gt;  &lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;🔹 Step 1: Emitting Domain Events in Microservices&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When an event occurs (e.g., a task is completed), the &lt;strong&gt;task service&lt;/strong&gt; should emit a &lt;code&gt;TaskCompletedEvent&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SetStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="n"&gt;newStatusId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt; &lt;span class="n"&gt;setAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StatusHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LastOrDefault&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;newStatusId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Prevent duplicate status updates&lt;/span&gt;

    &lt;span class="nf"&gt;AddStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;ValueObjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TaskStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newStatusId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setAt&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="nf"&gt;AddDomainEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TaskStatusChangedEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ValueObjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TaskStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Completed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;SetActualAccomplishDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;setAt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;AddDomainEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TaskCompletedEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;setAt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PlannedAccomplishDate&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;🔹 Step 2: Publishing Events to RabbitMQ&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We use &lt;strong&gt;RabbitMQ&lt;/strong&gt; to send events asynchronously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EventPublisher&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IConnection&lt;/span&gt; &lt;span class="n"&gt;_connection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IModel&lt;/span&gt; &lt;span class="n"&gt;_channel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;EventPublisher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConnectionFactory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;HostName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"localhost"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;_connection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateConnection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;_channel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateModel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="n"&gt;PublishEvent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;@event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDomainEvent&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;@event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BasicPublish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exchange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;routingKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"task-events"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;🔹 Step 3: Consuming Events in Analytics Service&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;background service&lt;/strong&gt; listens to RabbitMQ and &lt;strong&gt;updates the analytics database&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EventConsumer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BackgroundService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IServiceScopeFactory&lt;/span&gt; &lt;span class="n"&gt;_serviceScopeFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IModel&lt;/span&gt; &lt;span class="n"&gt;_channel&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;EventConsumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceScopeFactory&lt;/span&gt; &lt;span class="n"&gt;serviceScopeFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_serviceScopeFactory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serviceScopeFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ConnectionFactory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;HostName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"localhost"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateConnection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;_channel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateModel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;stoppingToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;consumer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EventingBasicConsumer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_channel&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;consumer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Received&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ea&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ea&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;eventBase&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EventBase&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_serviceScopeFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateScope&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;analyticsRepo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IAnalyticsRepository&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;analyticsRepo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ProcessEventAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;eventBase&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="n"&gt;_channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BasicConsume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"task-events"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;autoAck&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;consumer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;consumer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;🔹 Step 4: Storing Analytics Data in SQL Server&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Each event &lt;strong&gt;updates a real-time analytics table&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;InsertTaskCompletedAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TaskCompletedEvent&lt;/span&gt; &lt;span class="n"&gt;taskCompletedEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"
        INSERT INTO TaskAnalytics (TaskId, UserId, CompletedAt, PlannedAccomplishDate, OnTime)
        VALUES (@TaskId, @UserId, @CompletedAt, @PlannedAccomplishDate, 
                CASE WHEN @CompletedAt &amp;lt;= @PlannedAccomplishDate THEN 1 ELSE 0 END)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SqlConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_connectionString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;taskCompletedEvent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;3. Why This Approach is a Game Changer&lt;/strong&gt;
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Traditional Batch Analytics&lt;/th&gt;
&lt;th&gt;Event-Driven Real-Time Analytics&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (minutes/hours)&lt;/td&gt;
&lt;td&gt;Low (milliseconds/seconds)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hard to scale&lt;/td&gt;
&lt;td&gt;Scales automatically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Monolithic&lt;/td&gt;
&lt;td&gt;Microservices-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Freshness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stale (delayed)&lt;/td&gt;
&lt;td&gt;Real-time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;🚀 &lt;strong&gt;With event-driven microservices, analytics is truly real-time!&lt;/strong&gt;  &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;4. Final Thoughts: Why You Should Adopt This&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;💡 &lt;strong&gt;Scalability&lt;/strong&gt; → Works with thousands of concurrent users.&lt;br&gt;&lt;br&gt;
💡 &lt;strong&gt;Flexibility&lt;/strong&gt; → Decouples analytics from core services.&lt;br&gt;&lt;br&gt;
💡 &lt;strong&gt;Low Latency&lt;/strong&gt; → Streams data instead of batch jobs.&lt;br&gt;&lt;br&gt;
💡 &lt;strong&gt;Resilient&lt;/strong&gt; → Supports retries and fault tolerance.  &lt;/p&gt;

&lt;p&gt;If you're building &lt;strong&gt;data-driven applications&lt;/strong&gt;, using &lt;strong&gt;domain events + microservices&lt;/strong&gt; is &lt;strong&gt;the future&lt;/strong&gt; of real-time analytics. 🔥  &lt;/p&gt;

</description>
      <category>eventdriven</category>
      <category>analytics</category>
      <category>csharp</category>
    </item>
    <item>
      <title>How to Use ChatGPT as Your Study Partner for React</title>
      <dc:creator>Champ of Greatness</dc:creator>
      <pubDate>Wed, 05 Feb 2025 16:26:05 +0000</pubDate>
      <link>https://dev.to/champcbg/how-to-use-chatgpt-as-your-study-partner-for-react-58d4</link>
      <guid>https://dev.to/champcbg/how-to-use-chatgpt-as-your-study-partner-for-react-58d4</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Studying React can be challenging, but having an intelligent study partner can make the learning process smoother and more effective. ChatGPT can act as your personalized tutor, creating practice tests, grading your answers, and providing tailored study guides. This blog explores how to leverage ChatGPT to assess your knowledge, identify areas for improvement, and accelerate your learning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Request a React Practice Test
&lt;/h2&gt;

&lt;p&gt;A great way to evaluate your React knowledge is by asking ChatGPT to create a test. When prompted with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Can you create a React test for me to practice?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ChatGPT generated a comprehensive practice test with multiple-choice questions, theoretical questions, and coding challenges.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sample Questions from ChatGPT’s React Test
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Multiple-Choice Questions&lt;/strong&gt;
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;What is the correct way to update state in a class component?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a) &lt;code&gt;this.state = { count: count + 1 }&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;b) &lt;code&gt;this.setState({ count: this.state.count + 1 })&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;c) &lt;code&gt;this.updateState({ count: count + 1 })&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;d) &lt;code&gt;this.count = this.state.count + 1&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;What does the &lt;code&gt;useEffect&lt;/code&gt; hook do?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a) Handles asynchronous operations inside components&lt;/li&gt;
&lt;li&gt;b) Directly updates the DOM&lt;/li&gt;
&lt;li&gt;c) Provides state management in functional components&lt;/li&gt;
&lt;li&gt;d) Creates a new instance of a component&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Coding Challenge&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Convert this class component to a functional component using hooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;increment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Count: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;increment&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Increment&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By taking this test, you can gauge your proficiency in React.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Submit Your Answers for Evaluation
&lt;/h2&gt;

&lt;p&gt;Once you complete the test, you can input your answers into ChatGPT for grading. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. B
2. B
3. A
4. A
5. B
6. Props are passed in; state is managed in memory.
7. React's Virtual DOM does all the rendering and page navigation.
8. Don’t know.
9. That means nothing will be updated.
10. Don’t know.
11. Don’t know.
&amp;lt;Greeting name="John" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ChatGPT will then evaluate your answers, marking them as correct or incorrect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Review Your Score and Study Guide
&lt;/h2&gt;

&lt;p&gt;After grading your test, ChatGPT provides a detailed breakdown of your performance. In this case, the score was &lt;strong&gt;5 out of 12&lt;/strong&gt;, placing the user at an &lt;strong&gt;intermediate level&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Study Guide for Incorrect Answers&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Question 2: State Updates in React&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;Your Answer:&lt;/strong&gt; &lt;code&gt;The count increases by 2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Correct Answer:&lt;/strong&gt; &lt;code&gt;The count increases by 1&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt; React batches state updates in functional components. Calling &lt;code&gt;setCount(count + 1)&lt;/code&gt; twice doesn’t use the updated count value, so it only increments once. Instead, use a functional update:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevCount&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;prevCount&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevCount&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;prevCount&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Now it will increase by 2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Question 6: Props vs. State&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;Your Answer:&lt;/strong&gt; &lt;code&gt;Props are passed in; state is managed in memory.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Correct Answer:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Props:&lt;/strong&gt; Data passed from a parent to a child component (read-only).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State:&lt;/strong&gt; Data managed inside a component (mutable).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Question 7: Virtual DOM&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;Your Answer:&lt;/strong&gt; &lt;code&gt;Reacts virtual DOM does all the rendering and page navigation.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Correct Answer:&lt;/strong&gt; The Virtual DOM is a lightweight copy of the real DOM. React updates the Virtual DOM first, compares it with the previous version, and only updates the changed parts in the real DOM.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Question 8: Controlled vs. Uncontrolled Components&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;Your Answer:&lt;/strong&gt; (Not answered)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Correct Answer:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Controlled Components:&lt;/strong&gt; Store input data in React state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uncontrolled Components:&lt;/strong&gt; Use the DOM to store data (via &lt;code&gt;useRef&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Question 9: &lt;code&gt;useEffect([])&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;Your Answer:&lt;/strong&gt; &lt;code&gt;That means nothing will be updated.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Correct Answer:&lt;/strong&gt; &lt;code&gt;useEffect(() =&amp;gt; {...}, [])&lt;/code&gt; runs only &lt;strong&gt;once&lt;/strong&gt; when the component mounts, making it useful for initial API calls or event listeners.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Question 10: React Reconciliation&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;Your Answer:&lt;/strong&gt; (Not answered)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Correct Answer:&lt;/strong&gt; React’s diffing algorithm updates only the parts of the DOM that changed, making it efficient.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Question 11: Convert Class Component to Functional Component&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;❌ &lt;strong&gt;Your Answer:&lt;/strong&gt; (Not answered)&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Correct Answer:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Count: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Increment&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Retake the Test and Improve
&lt;/h2&gt;

&lt;p&gt;Now that you have a targeted study guide, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Review key concepts with ChatGPT.&lt;/li&gt;
&lt;li&gt;Ask for further explanations or examples.&lt;/li&gt;
&lt;li&gt;Retake a new practice test to measure improvement.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;ChatGPT is a powerful study partner for React learners. By generating practice tests, grading answers, and providing personalized study guides, it helps you understand and master React concepts efficiently. If you want to track your progress, repeat this process periodically and challenge yourself with more advanced tests.&lt;/p&gt;

&lt;p&gt;Ready to take your React skills to the next level? Ask ChatGPT for a new practice test today! 🚀&lt;/p&gt;

</description>
      <category>react</category>
      <category>chatgpt</category>
      <category>learning</category>
      <category>programming</category>
    </item>
    <item>
      <title>Optimistic Concurrency in React Before useOptimistic</title>
      <dc:creator>Champ of Greatness</dc:creator>
      <pubDate>Wed, 05 Feb 2025 13:22:50 +0000</pubDate>
      <link>https://dev.to/champcbg/optimistic-concurrency-in-react-before-useoptimistic-2omj</link>
      <guid>https://dev.to/champcbg/optimistic-concurrency-in-react-before-useoptimistic-2omj</guid>
      <description>&lt;p&gt;Optimistic concurrency is a powerful technique that enhances user experience by providing instant feedback before an operation is confirmed by the server. Before React 19 introduced useOptimistic, developers had to rely on various workarounds to achieve this behavior.&lt;/p&gt;

&lt;p&gt;In this article, we will explore the most common approaches used in older versions of React to handle optimistic updates, their advantages, and their limitations.&lt;/p&gt;

&lt;p&gt;🔍 What is Optimistic Concurrency?&lt;/p&gt;

&lt;p&gt;Optimistic concurrency assumes that an operation will succeed and updates the UI immediately without waiting for server confirmation. If the operation fails, the UI is rolled back to the previous state.&lt;/p&gt;

&lt;p&gt;🚀 Approaches Before useOptimistic&lt;/p&gt;

&lt;p&gt;1️⃣ Using useState for Temporary UI Updates&lt;/p&gt;

&lt;p&gt;One of the simplest ways to implement optimistic updates in React is by using useState. Here’s how it works:&lt;/p&gt;

&lt;p&gt;Example: Optimistic Update with useState&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState } from 'react';

function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: 'Learn React' },
    { id: 2, text: 'Build a Project' },
  ]);

  function handleAddTodo() {
    const newTodo = { id: Date.now(), text: 'New Todo' };

    // Optimistically update UI
    setTodos((prev) =&amp;gt; [...prev, newTodo]);

    // Simulate API call
    fetch('/api/todos', {
      method: 'POST',
      body: JSON.stringify(newTodo),
    }).catch(() =&amp;gt; {
      // Rollback on error
      setTodos((prev) =&amp;gt; prev.filter((todo) =&amp;gt; todo.id !== newTodo.id));
    });
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;button onClick={handleAddTodo}&amp;gt;Add Todo&amp;lt;/button&amp;gt;
      &amp;lt;ul&amp;gt;
        {todos.map((todo) =&amp;gt; (
          &amp;lt;li key={todo.id}&amp;gt;{todo.text}&amp;lt;/li&amp;gt;
        ))}
      &amp;lt;/ul&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Pros:&lt;/p&gt;

&lt;p&gt;Simple and easy to implement&lt;/p&gt;

&lt;p&gt;Provides immediate feedback to the user&lt;/p&gt;

&lt;p&gt;❌ Cons:&lt;/p&gt;

&lt;p&gt;Manual rollback is required if the API call fails&lt;/p&gt;

&lt;p&gt;Causes unnecessary re-renders when state updates&lt;/p&gt;

&lt;p&gt;Difficult to scale for complex state management&lt;/p&gt;

&lt;p&gt;2️⃣ Using React Query’s onMutate&lt;/p&gt;

&lt;p&gt;For applications that rely on external APIs, React Query was a popular choice before useOptimistic. React Query provides a way to optimistically update the UI and roll back changes on failure.&lt;/p&gt;

&lt;p&gt;Example: React Query Optimistic Update&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';

function useAddTodo() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (newTodo) =&amp;gt; axios.post('/api/todos', newTodo),
    onMutate: async (newTodo) =&amp;gt; {
      // Cancel any outgoing queries
      await queryClient.cancelQueries(['todos']);

      // Snapshot of previous todos
      const previousTodos = queryClient.getQueryData(['todos']);

      // Optimistically update UI
      queryClient.setQueryData(['todos'], (oldTodos) =&amp;gt; [...oldTodos, newTodo]);

      return { previousTodos };
    },
    onError: (_, __, context) =&amp;gt; {
      // Rollback to previous state on error
      queryClient.setQueryData(['todos'], context.previousTodos);
    },
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Pros:&lt;/p&gt;

&lt;p&gt;Automatically manages API state&lt;/p&gt;

&lt;p&gt;Reduces unnecessary API calls&lt;/p&gt;

&lt;p&gt;Built-in rollback mechanism&lt;/p&gt;

&lt;p&gt;❌ Cons:&lt;/p&gt;

&lt;p&gt;Requires an external library&lt;/p&gt;

&lt;p&gt;More complex to set up&lt;/p&gt;

&lt;p&gt;Still involves direct mutation of the cache&lt;/p&gt;

&lt;p&gt;3️⃣ Using Redux for Optimistic Updates&lt;/p&gt;

&lt;p&gt;Before React Query became popular, Redux was widely used to manage global state. Optimistic updates in Redux involved updating the store immediately and dispatching a rollback action if the API call failed.&lt;/p&gt;

&lt;p&gt;Example: Optimistic Update with Redux&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createSlice, configureStore } from '@reduxjs/toolkit';

const todosSlice = createSlice({
  name: 'todos',
  initialState: [],
  reducers: {
    addTodoOptimistic: (state, action) =&amp;gt; {
      state.push(action.payload);
    },
    rollbackTodo: (state, action) =&amp;gt; {
      return state.filter(todo =&amp;gt; todo.id !== action.payload.id);
    },
  },
});

const store = configureStore({ reducer: { todos: todosSlice.reducer } });

// Simulate API call in a component
function handleAddTodo() {
  const newTodo = { id: Date.now(), text: 'New Todo' };
  store.dispatch(todosSlice.actions.addTodoOptimistic(newTodo));

  fetch('/api/todos', {
    method: 'POST',
    body: JSON.stringify(newTodo),
  }).catch(() =&amp;gt; {
    store.dispatch(todosSlice.actions.rollbackTodo(newTodo));
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Pros:&lt;/p&gt;

&lt;p&gt;Works well for complex global state management&lt;/p&gt;

&lt;p&gt;Redux DevTools make debugging easier&lt;/p&gt;

&lt;p&gt;❌ Cons:&lt;/p&gt;

&lt;p&gt;Requires boilerplate code&lt;/p&gt;

&lt;p&gt;Overhead for simple state management&lt;/p&gt;

&lt;p&gt;🎯 How useOptimistic Improves This&lt;/p&gt;

&lt;p&gt;React 19’s useOptimistic provides a much cleaner and simpler way to handle optimistic updates. Instead of modifying state directly, useOptimistic allows you to derive a temporary optimistic state without affecting the actual state.&lt;/p&gt;

&lt;p&gt;Example: useOptimistic (React 19)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState, useOptimistic } from 'react';

function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: 'Learn React 19' },
  ]);

  const [optimisticTodos, addOptimisticTodo] = useOptimistic(
    todos,
    (state, newTodo) =&amp;gt; [...state, newTodo]
  );

  function handleAddTodo() {
    const newTodo = { id: Date.now(), text: 'New Todo' };

    addOptimisticTodo(newTodo);
    setTodos((prev) =&amp;gt; [...prev, newTodo]);
  }

  return (
    &amp;lt;ul&amp;gt;
      {optimisticTodos.map((todo) =&amp;gt; (
        &amp;lt;li key={todo.id}&amp;gt;{todo.text}&amp;lt;/li&amp;gt;
      ))}
    &amp;lt;/ul&amp;gt;
  );
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ Benefits of useOptimistic:&lt;/p&gt;

&lt;p&gt;No direct state mutation&lt;/p&gt;

&lt;p&gt;UI updates instantly without re-rendering&lt;/p&gt;

&lt;p&gt;No manual rollback needed&lt;/p&gt;

&lt;p&gt;📌 Conclusion&lt;/p&gt;

&lt;p&gt;Before React 19, optimistic concurrency was achieved through useState, React Query, or Redux. Each method had its drawbacks, such as manual rollbacks, direct state mutations, and additional dependencies. With useOptimistic, React now provides a built-in, cleaner, and more efficient way to handle optimistic updates, making UI interactions smoother than ever.&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>reactjsdevelopment</category>
    </item>
  </channel>
</rss>
