<?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: Harpreet Singh</title>
    <description>The latest articles on DEV Community by Harpreet Singh (@dev_harry).</description>
    <link>https://dev.to/dev_harry</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%2F3443988%2F59be7fbf-2bea-408e-8237-5814b02a065c.jpg</url>
      <title>DEV Community: Harpreet Singh</title>
      <link>https://dev.to/dev_harry</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dev_harry"/>
    <language>en</language>
    <item>
      <title>GitHub Copilot's New AI Credits Explained (Without the Corporate Buzzwords)</title>
      <dc:creator>Harpreet Singh</dc:creator>
      <pubDate>Tue, 02 Jun 2026 04:38:29 +0000</pubDate>
      <link>https://dev.to/dev_harry/github-copilots-new-ai-credits-explained-without-the-corporate-buzzwords-4cm7</link>
      <guid>https://dev.to/dev_harry/github-copilots-new-ai-credits-explained-without-the-corporate-buzzwords-4cm7</guid>
      <description>&lt;p&gt;The internet seems to have collectively lost its mind over GitHub Copilot's new AI Credits system.&lt;/p&gt;

&lt;p&gt;After reading dozens of Reddit threads, GitHub documentation, and angry comments, I've realized most developers aren't upset because the pricing changed.&lt;/p&gt;

&lt;p&gt;They're upset because nobody understands it.&lt;/p&gt;

&lt;p&gt;So here's the simplest explanation I can give.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Old Copilot
&lt;/h2&gt;

&lt;p&gt;Life was easy.&lt;/p&gt;

&lt;p&gt;You paid your monthly subscription.&lt;/p&gt;

&lt;p&gt;You used Copilot.&lt;/p&gt;

&lt;p&gt;Nobody cared how many prompts you sent.&lt;/p&gt;

&lt;p&gt;Nobody checked a dashboard.&lt;/p&gt;

&lt;p&gt;Nobody thought about "consumption."&lt;/p&gt;

&lt;p&gt;Copilot was basically Netflix.&lt;/p&gt;

&lt;p&gt;Pay once. Watch as much as you want.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Copilot
&lt;/h2&gt;

&lt;p&gt;Now you get a monthly credit allowance.&lt;/p&gt;

&lt;p&gt;Every AI interaction consumes some amount of those credits.&lt;/p&gt;

&lt;p&gt;The amount depends on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which model you use&lt;/li&gt;
&lt;li&gt;How much context you send&lt;/li&gt;
&lt;li&gt;Whether you're using chat, agent mode, or code review&lt;/li&gt;
&lt;li&gt;How much work the model performs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words:&lt;/p&gt;

&lt;p&gt;Not all prompts cost the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why People Are Freaking Out
&lt;/h2&gt;

&lt;p&gt;Imagine buying unlimited coffee for years.&lt;/p&gt;

&lt;p&gt;One day the coffee shop says:&lt;/p&gt;

&lt;p&gt;"Good news! We're introducing Coffee Credits."&lt;/p&gt;

&lt;p&gt;Now every drink has a different price.&lt;/p&gt;

&lt;p&gt;Small coffee = 1 credit&lt;/p&gt;

&lt;p&gt;Large coffee = 5 credits&lt;/p&gt;

&lt;p&gt;Fancy coffee = 20 credits&lt;/p&gt;

&lt;p&gt;Nobody knows the price until after they order.&lt;/p&gt;

&lt;p&gt;That's basically how developers feel right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hidden Problem
&lt;/h2&gt;

&lt;p&gt;The issue isn't that credits exist.&lt;/p&gt;

&lt;p&gt;Most AI products already have usage-based pricing.&lt;/p&gt;

&lt;p&gt;The issue is predictability.&lt;/p&gt;

&lt;p&gt;Developers want answers to simple questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How many prompts do I get?&lt;/li&gt;
&lt;li&gt;How much will a coding session cost?&lt;/li&gt;
&lt;li&gt;How many days will my credits last?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Right now many developers have no idea.&lt;/p&gt;

&lt;p&gt;And uncertainty feels expensive.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Burns Credits Fast?
&lt;/h2&gt;

&lt;p&gt;Based on community reports, the biggest credit consumers appear to be:&lt;/p&gt;

&lt;h3&gt;
  
  
  Agent Mode
&lt;/h3&gt;

&lt;p&gt;When an AI agent explores files, analyzes code, runs multiple reasoning steps, and generates large changes, consumption can increase quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Large Context Windows
&lt;/h3&gt;

&lt;p&gt;Sending an entire codebase is dramatically different from sending a single function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Premium Models
&lt;/h3&gt;

&lt;p&gt;Not all models are priced equally.&lt;/p&gt;

&lt;p&gt;Some reasoning-focused models consume significantly more credits than lightweight models.&lt;/p&gt;

&lt;h3&gt;
  
  
  Repeated Iteration
&lt;/h3&gt;

&lt;p&gt;"Try again."&lt;/p&gt;

&lt;p&gt;"One more thing."&lt;/p&gt;

&lt;p&gt;"Can you improve this?"&lt;/p&gt;

&lt;p&gt;Those innocent follow-up requests add up.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Most Developers Will Probably Do
&lt;/h2&gt;

&lt;p&gt;I suspect we'll see three groups emerge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Group 1: Casual Users
&lt;/h3&gt;

&lt;p&gt;These people will barely notice.&lt;/p&gt;

&lt;p&gt;Autocomplete and occasional chat requests won't come close to exhausting their allowance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Group 2: Heavy AI Users
&lt;/h3&gt;

&lt;p&gt;These are developers who spend all day with agents, code reviews, architecture discussions, and large refactors.&lt;/p&gt;

&lt;p&gt;They're going to hit limits much faster than expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Group 3: AI Optimizers
&lt;/h3&gt;

&lt;p&gt;This group will start mixing tools.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ChatGPT for planning&lt;/li&gt;
&lt;li&gt;Claude for reasoning&lt;/li&gt;
&lt;li&gt;Copilot for IDE integration&lt;/li&gt;
&lt;li&gt;Local models for experimentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essentially treating AI like a toolbox instead of a single platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Take
&lt;/h2&gt;

&lt;p&gt;The new system isn't necessarily bad.&lt;/p&gt;

&lt;p&gt;But GitHub has accidentally introduced something developers hate:&lt;/p&gt;

&lt;p&gt;Budgeting.&lt;/p&gt;

&lt;p&gt;For years, Copilot felt like a utility.&lt;/p&gt;

&lt;p&gt;Now it feels like a cloud service.&lt;/p&gt;

&lt;p&gt;And cloud services always lead to the same question:&lt;/p&gt;

&lt;p&gt;"Wait... how much did that cost?"&lt;/p&gt;

&lt;p&gt;The real challenge for GitHub isn't pricing.&lt;/p&gt;

&lt;p&gt;It's making developers feel confident that they understand the pricing.&lt;/p&gt;

&lt;p&gt;Because right now, most of us are staring at AI credits the same way we stare at AWS bills:&lt;/p&gt;

&lt;p&gt;Slightly confused.&lt;/p&gt;

&lt;p&gt;A little concerned.&lt;/p&gt;

&lt;p&gt;And afraid to click refresh.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>github</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>GitHub Copilot Just Invented Loot Boxes for Coding</title>
      <dc:creator>Harpreet Singh</dc:creator>
      <pubDate>Tue, 02 Jun 2026 04:20:47 +0000</pubDate>
      <link>https://dev.to/dev_harry/github-copilot-just-invented-loot-boxes-for-coding-2e0</link>
      <guid>https://dev.to/dev_harry/github-copilot-just-invented-loot-boxes-for-coding-2e0</guid>
      <description>&lt;p&gt;Remember when GitHub Copilot was simple?&lt;/p&gt;

&lt;p&gt;You paid your monthly subscription.&lt;br&gt;
You wrote code.&lt;br&gt;
The AI occasionally hallucinated.&lt;br&gt;
Everyone moved on with their lives.&lt;/p&gt;

&lt;p&gt;Now I need a finance degree to understand whether asking Claude to rename a variable will cost me three credits, thirty credits, or my firstborn child.&lt;/p&gt;

&lt;p&gt;GitHub's new AI Credits system officially landed this month, replacing the old request-based model with token-based billing. On paper, it sounds reasonable:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Pay for what you use."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In reality, it feels more like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Good luck figuring out what you used."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Developers across Reddit are posting screenshots showing massive chunks of their monthly allowance disappearing after only a handful of agent requests, code reviews, or debugging sessions.&lt;/p&gt;

&lt;p&gt;The funniest part?&lt;/p&gt;

&lt;p&gt;For the last two years, AI companies trained us to write better prompts.&lt;/p&gt;

&lt;p&gt;"Give more context."&lt;br&gt;
"Provide the entire codebase."&lt;br&gt;
"Let the agent reason deeply."&lt;br&gt;
"Use autonomous workflows."&lt;/p&gt;

&lt;p&gt;Now the bill arrives and suddenly every extra token feels like ordering guacamole at the airport.&lt;/p&gt;

&lt;p&gt;We're being financially punished for following the best practices they taught us.&lt;/p&gt;

&lt;p&gt;The old Copilot experience was predictable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pay monthly&lt;/li&gt;
&lt;li&gt;Use it constantly&lt;/li&gt;
&lt;li&gt;Never think about accounting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The new experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open billing dashboard&lt;/li&gt;
&lt;li&gt;Watch credits evaporate&lt;/li&gt;
&lt;li&gt;Start calculating token economics&lt;/li&gt;
&lt;li&gt;Wonder if asking a follow-up question is worth it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some developers have already started moving heavy reasoning work back to ChatGPT, Claude, Cursor, Cline, or local models while using Copilot only for IDE convenience.&lt;/p&gt;

&lt;p&gt;And honestly?&lt;/p&gt;

&lt;p&gt;That's probably the unintended consequence.&lt;/p&gt;

&lt;p&gt;GitHub spent years convincing us that AI should be embedded into every step of software development.&lt;/p&gt;

&lt;p&gt;Now they're teaching us to ration prompts like we're living through the Great Token Depression.&lt;/p&gt;

&lt;p&gt;The most ironic outcome is that Copilot might accidentally make developers better engineers.&lt;/p&gt;

&lt;p&gt;Because before every prompt we'll ask:&lt;/p&gt;

&lt;p&gt;"Do I really need AI for this?"&lt;/p&gt;

&lt;p&gt;And for the first time since 2023, the answer might be:&lt;/p&gt;

&lt;p&gt;"No, I can probably write the damn function myself."&lt;/p&gt;

</description>
      <category>ai</category>
      <category>githubcopilot</category>
      <category>programming</category>
      <category>github</category>
    </item>
    <item>
      <title>The Worst Time to Quit Software Engineering Might Be Right Now</title>
      <dc:creator>Harpreet Singh</dc:creator>
      <pubDate>Thu, 28 May 2026 01:18:28 +0000</pubDate>
      <link>https://dev.to/dev_harry/the-worst-time-to-quit-software-engineering-might-be-right-now-229h</link>
      <guid>https://dev.to/dev_harry/the-worst-time-to-quit-software-engineering-might-be-right-now-229h</guid>
      <description>&lt;p&gt;I understand why so many people are questioning software engineering right now.&lt;/p&gt;

&lt;p&gt;Every week there’s another headline saying AI will replace developers.&lt;/p&gt;

&lt;p&gt;Junior engineers are worried there won’t be jobs.&lt;br&gt;
Senior engineers are wondering how long their experience will stay valuable.&lt;br&gt;
And honestly, if you spend enough time on tech Twitter or LinkedIn, it can start feeling like the industry is collapsing in real time.&lt;/p&gt;

&lt;p&gt;But after using AI heavily in my day-to-day work as a software engineer, I’ve started seeing things differently.&lt;/p&gt;

&lt;p&gt;AI didn’t make me feel less useful.&lt;/p&gt;

&lt;p&gt;It made me feel more capable.&lt;/p&gt;




&lt;p&gt;Before AI became part of my workflow, a lot of engineering time disappeared into things that were mentally draining but necessary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;repetitive refactoring&lt;/li&gt;
&lt;li&gt;debugging small issues&lt;/li&gt;
&lt;li&gt;writing boilerplate&lt;/li&gt;
&lt;li&gt;digging through documentation&lt;/li&gt;
&lt;li&gt;trying to remember syntax&lt;/li&gt;
&lt;li&gt;cleaning up legacy code&lt;/li&gt;
&lt;li&gt;writing SQL queries&lt;/li&gt;
&lt;li&gt;optimizing simple functions&lt;/li&gt;
&lt;li&gt;translating vague tickets into technical tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these tasks were impossible.&lt;/p&gt;

&lt;p&gt;They were just time-consuming.&lt;/p&gt;

&lt;p&gt;Now, a lot of that friction is reduced dramatically.&lt;/p&gt;




&lt;p&gt;One of the biggest changes I noticed was backlog cleanup.&lt;/p&gt;

&lt;p&gt;Tasks that used to sit untouched because nobody wanted to deal with them suddenly became manageable.&lt;/p&gt;

&lt;p&gt;Not because AI magically solved everything.&lt;/p&gt;

&lt;p&gt;But because it helped reduce the “mental startup cost” of difficult tasks.&lt;/p&gt;

&lt;p&gt;Sometimes all you need is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a starting point&lt;/li&gt;
&lt;li&gt;a refactored example&lt;/li&gt;
&lt;li&gt;help understanding unfamiliar code&lt;/li&gt;
&lt;li&gt;a faster debugging path&lt;/li&gt;
&lt;li&gt;quick documentation summaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That momentum matters more than people realize.&lt;/p&gt;

&lt;p&gt;A task that feels overwhelming at 9AM suddenly becomes achievable when AI helps break it down.&lt;/p&gt;




&lt;p&gt;I also noticed we started delivering faster as a team.&lt;/p&gt;

&lt;p&gt;Not in a “replace developers with AI” kind of way.&lt;/p&gt;

&lt;p&gt;More in a:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;less context switching&lt;/li&gt;
&lt;li&gt;faster research&lt;/li&gt;
&lt;li&gt;quicker prototyping&lt;/li&gt;
&lt;li&gt;fewer hours stuck on repetitive problems&lt;/li&gt;
&lt;li&gt;better ticket breakdowns&lt;/li&gt;
&lt;li&gt;improved communication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;kind of way.&lt;/p&gt;

&lt;p&gt;The interesting part is that AI didn’t just help with coding.&lt;/p&gt;

&lt;p&gt;It helped with thinking.&lt;/p&gt;




&lt;p&gt;I’ve genuinely started thinking more like a project manager because of AI.&lt;/p&gt;

&lt;p&gt;Not because I stopped engineering.&lt;/p&gt;

&lt;p&gt;But because I spend less time fighting small implementation details and more time thinking about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;priorities&lt;/li&gt;
&lt;li&gt;tradeoffs&lt;/li&gt;
&lt;li&gt;user impact&lt;/li&gt;
&lt;li&gt;scalability&lt;/li&gt;
&lt;li&gt;timelines&lt;/li&gt;
&lt;li&gt;technical debt&lt;/li&gt;
&lt;li&gt;delivery strategy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI handles enough of the repetitive workload that I have more mental space for higher-level decisions.&lt;/p&gt;

&lt;p&gt;And honestly, I think that’s where software engineering is heading.&lt;/p&gt;

&lt;p&gt;The value of developers won’t disappear.&lt;/p&gt;

&lt;p&gt;The value will shift upward.&lt;/p&gt;




&lt;p&gt;The engineers who become most valuable over the next few years probably won’t be the ones writing code completely manually.&lt;/p&gt;

&lt;p&gt;They’ll be the ones who know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how to guide AI effectively&lt;/li&gt;
&lt;li&gt;how to validate output&lt;/li&gt;
&lt;li&gt;how to make architectural decisions&lt;/li&gt;
&lt;li&gt;how to communicate clearly&lt;/li&gt;
&lt;li&gt;how to combine engineering with product thinking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because generating code is only one part of building software.&lt;/p&gt;

&lt;p&gt;Real-world engineering still involves ambiguity, responsibility, tradeoffs, and human decisions.&lt;/p&gt;

&lt;p&gt;AI helps with execution.&lt;/p&gt;

&lt;p&gt;But humans still decide direction.&lt;/p&gt;




&lt;p&gt;I honestly think this is one of the most exciting times to become a software engineer.&lt;/p&gt;

&lt;p&gt;Not because AI makes the job easy.&lt;/p&gt;

&lt;p&gt;But because developers now have tools that can massively increase their leverage.&lt;/p&gt;

&lt;p&gt;The industry is changing fast.&lt;br&gt;
That part is true.&lt;/p&gt;

&lt;p&gt;But software engineering has always evolved.&lt;/p&gt;

&lt;p&gt;And the people willing to adapt usually end up ahead.&lt;/p&gt;

&lt;p&gt;So if you’re considering software engineering as a career, don’t focus only on whether AI can write code.&lt;/p&gt;

&lt;p&gt;Focus on whether you’re learning how to use the most powerful development tools we’ve ever had.&lt;/p&gt;

&lt;p&gt;Because the worst time to quit software engineering might be exactly when the industry is transforming the fastest.&lt;/p&gt;

</description>
      <category>career</category>
      <category>ai</category>
      <category>softwareengineering</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Built a VS Code Extension to Detect Missing i18n Keys Before Production</title>
      <dc:creator>Harpreet Singh</dc:creator>
      <pubDate>Thu, 09 Apr 2026 01:56:34 +0000</pubDate>
      <link>https://dev.to/dev_harry/i-built-a-vs-code-extension-to-detect-missing-i18n-keys-before-production-1a3c</link>
      <guid>https://dev.to/dev_harry/i-built-a-vs-code-extension-to-detect-missing-i18n-keys-before-production-1a3c</guid>
      <description>&lt;p&gt;if you’ve ever shipped a UI bug where users see something like this:&lt;/p&gt;

&lt;p&gt;settings.labels.confirm_action_final_v2_FIXED&lt;/p&gt;

&lt;p&gt;…then you already know how painful localization bugs can be.&lt;/p&gt;

&lt;p&gt;Missing i18n keys are one of those problems that seem small until they hit production. They slip through code review, hide inside large translation files, and usually appear when it’s too late.&lt;/p&gt;

&lt;p&gt;After dealing with this frustration too many times, I decided to build a better solution.&lt;/p&gt;

&lt;p&gt;The Problem&lt;/p&gt;

&lt;p&gt;In multilingual apps, it’s easy to:&lt;/p&gt;

&lt;p&gt;Reference translation keys that don’t exist&lt;br&gt;
Leave hardcoded untranslated strings in components&lt;br&gt;
Break localization during refactoring without noticing&lt;/p&gt;

&lt;p&gt;Most teams catch these issues manually, which is slow, error-prone, and honestly exhausting.&lt;/p&gt;

&lt;p&gt;The Solution: Localization Scanner for VS Code&lt;/p&gt;

&lt;p&gt;I created a VS Code extension that scans your project and detects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Missing translation keys&lt;/li&gt;
&lt;li&gt;Broken localization references&lt;/li&gt;
&lt;li&gt;Hardcoded untranslated UI strings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of manually searching JSON files and comparing keys, the extension flags issues directly inside VS Code while you work.&lt;/p&gt;

&lt;p&gt;Why I Built It&lt;/p&gt;

&lt;p&gt;I wanted something that would:&lt;/p&gt;

&lt;p&gt;Save debugging time&lt;br&gt;
Prevent embarrassing production bugs&lt;br&gt;
Help teams catch localization issues earlier in development&lt;/p&gt;

&lt;p&gt;Localization should be automatic and reliable not a guessing game.&lt;/p&gt;

&lt;p&gt;Who It’s For&lt;/p&gt;

&lt;p&gt;This tool is useful if you build apps with:&lt;/p&gt;

&lt;p&gt;React&lt;br&gt;
Vue&lt;br&gt;
Next.js&lt;br&gt;
Any frontend app using i18n translation files&lt;br&gt;
What’s Next&lt;/p&gt;

&lt;p&gt;I’m actively improving the extension and would love feedback from developers working on multilingual apps.&lt;/p&gt;

&lt;p&gt;What’s the most frustrating localization bug you’ve dealt with?&lt;/p&gt;

&lt;p&gt;Read more and try it here: &lt;a href="https://dev.to/dev_harry/how-to-find-missing-i18n-keys-without-losing-your-mind-or-your-job-1jff"&gt;https://dev.to/dev_harry/how-to-find-missing-i18n-keys-without-losing-your-mind-or-your-job-1jff&lt;/a&gt;&lt;br&gt;
or download it here&lt;br&gt;
&lt;a href="https://marketplace.visualstudio.com/items?itemName=devharry.localizations-scanner" rel="noopener noreferrer"&gt;https://marketplace.visualstudio.com/items?itemName=devharry.localizations-scanner&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to find missing i18n keys without losing your mind (or your job)</title>
      <dc:creator>Harpreet Singh</dc:creator>
      <pubDate>Thu, 12 Mar 2026 00:03:54 +0000</pubDate>
      <link>https://dev.to/dev_harry/how-to-find-missing-i18n-keys-without-losing-your-mind-or-your-job-1jff</link>
      <guid>https://dev.to/dev_harry/how-to-find-missing-i18n-keys-without-losing-your-mind-or-your-job-1jff</guid>
      <description>&lt;p&gt;Look, we’ve all been there. You just pushed a massive feature, you’re feeling like a genius, and then the "urgent" Slack message hits.&lt;/p&gt;

&lt;p&gt;Your manager sends a screenshot from the production site, and instead of a nice "Submit" button, the users are seeing:&lt;/p&gt;

&lt;p&gt;{{ settings.labels.confirm_action_final_v2_FIXED }}&lt;/p&gt;

&lt;p&gt;It’s embarrassing. It looks like the code is leaking. And honestly, trying to keep your source code and your en.json file in sync manually is a one-way ticket to burnout. I got tired of the manual "Find in Files" dance, so I built a tool to handle it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Introducing Localization Scanner&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I created this &lt;strong&gt;VS Code extension&lt;/strong&gt; because I wanted something that stayed out of the way until I actually needed it. It’s basically a safety net for your i18n workflow.&lt;/p&gt;

&lt;p&gt;What it actually does for you:&lt;/p&gt;

&lt;p&gt;Finds the "Ghosts": It scans your code and tells you exactly which keys you used that don't actually exist in your translation files.&lt;/p&gt;

&lt;p&gt;The "Did I really type that?" Detector: It finds hardcoded strings that you forgot to wrap in a translation function. It’s smart enough to know that className="mb-4" isn't a translation, but "Are you sure you want to delete this?" definitely is.&lt;/p&gt;

&lt;p&gt;Respects your RAM: I hate extensions that slow down my IDE. This has zero performance impact because it only runs when you hit the refresh button.&lt;/p&gt;

&lt;p&gt;Click and Fix: You see a missing key in the sidebar, you click it, and it takes you right to the line where you missed it.&lt;/p&gt;

&lt;p&gt;Check it out&lt;br&gt;
If you're working with React, Vue, or Next.js and you’re tired of shipping raw JSON keys to your users, give this a go. It’s open source and easy to set up with a tiny scan.json file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=devharry.localizations-scanner" rel="noopener noreferrer"&gt;Marketplace&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Happybajwa/localizations-scanner" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please download it, use it, and leave a nice review. If I see a 5-star rating, I might actually finish my other 14 pending side projects. If you leave a bad one, I’m telling my mom. lol. But seriously, your feedback keeps me going! &lt;/p&gt;

&lt;p&gt;Let me know if it saves you from a "broken UI" bug report. I'd love to hear your feedback or see a PR if you want to add more features!&lt;/p&gt;

&lt;h1&gt;
  
  
  vscodextensions #i18n #webdev #typescript #javascript #programming
&lt;/h1&gt;

</description>
      <category>react</category>
      <category>i18</category>
      <category>localizations</category>
      <category>extensions</category>
    </item>
    <item>
      <title>Blob Storage as a Database: Dumb Idea or Underrated Trick?</title>
      <dc:creator>Harpreet Singh</dc:creator>
      <pubDate>Mon, 20 Oct 2025 01:53:31 +0000</pubDate>
      <link>https://dev.to/dev_harry/blob-storage-as-a-database-dumb-idea-or-underrated-trick-1n9p</link>
      <guid>https://dev.to/dev_harry/blob-storage-as-a-database-dumb-idea-or-underrated-trick-1n9p</guid>
      <description>&lt;p&gt;&lt;strong&gt;Subtitle:&lt;/strong&gt; Why I Tried Storing Structured Data in Blobs and What Actually Happened&lt;/p&gt;




&lt;h3&gt;
  
  
  1. The Hook
&lt;/h3&gt;

&lt;p&gt;Everyone tells you &lt;strong&gt;not&lt;/strong&gt; to use Azure Blob Storage as a database. Naturally, I decided to try it anyway.&lt;/p&gt;

&lt;p&gt;I had a use case that didn’t justify spinning up SQL or Cosmos DB just some structured, semi-static JSON data I needed to persist cheaply and access globally. Blob Storage seemed too simple to ignore.&lt;/p&gt;

&lt;p&gt;So I asked myself: &lt;em&gt;Could I push Blob Storage to act like a database?&lt;/em&gt; Spoiler: it kind of worked.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. The Concept
&lt;/h3&gt;

&lt;p&gt;Here’s the basic idea:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each record is a &lt;strong&gt;blob&lt;/strong&gt; (for example, a JSON file).&lt;/li&gt;
&lt;li&gt;Each container is a &lt;strong&gt;logical table&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The blob name acts as a &lt;strong&gt;primary key&lt;/strong&gt; (like &lt;code&gt;users/user-123.json&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Blob versioning gives you &lt;strong&gt;time-travel&lt;/strong&gt; or history for free.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A minimal .NET example:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;blob&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;containerClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBlobClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"users/&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="s"&gt;.json"&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;blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UploadAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BinaryData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromObjectAsJson&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can later read it back:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;blob&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;containerClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBlobClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"users/&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="s"&gt;.json"&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;data&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;blob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DownloadContentAsync&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;data&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;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ToObjectFromJson&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No schema, no migrations, no SQL. Just blobs.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. The Benefits (Why It’s Not Completely Dumb)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ridiculously cheap&lt;/strong&gt;: fractions of a cent per GB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Globally replicated&lt;/strong&gt;: can integrate with CDN.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema-flexible&lt;/strong&gt;: store any JSON structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in versioning&lt;/strong&gt;: blob versioning acts like record history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Excellent for static or rarely-mutating data&lt;/strong&gt;: configurations, static datasets, logs, or precomputed AI outputs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Real-world use case: I stored pre-rendered dashboard snapshots in Blob Storage and served them directly through a CDN. Instant load times, no compute, no database calls.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. The Pain Points (Why It &lt;em&gt;Is&lt;/em&gt; Kinda Dumb)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No query engine&lt;/strong&gt;: You can’t filter or search without loading everything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No transactions&lt;/strong&gt; or concurrency handling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High latency&lt;/strong&gt;: Every read/write is a network call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited atomic operations&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadata scaling&lt;/strong&gt;: Listing blobs becomes slow and costly after tens of thousands.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At around 50,000 blobs, listing performance dropped noticeably. I ended up adding a lightweight Redis index to keep track of blob keys.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. The Hybrid Trick
&lt;/h3&gt;

&lt;p&gt;Here’s where it got interesting.&lt;/p&gt;

&lt;p&gt;What if Blob Storage isn’t &lt;em&gt;the&lt;/em&gt; database, but &lt;em&gt;a tier&lt;/em&gt; of one?&lt;/p&gt;

&lt;p&gt;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;App -&amp;gt; Redis index -&amp;gt; Blob for data payload -&amp;gt; CDN edge cache
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This hybrid approach works beautifully:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis stores keys and metadata for fast lookups.&lt;/li&gt;
&lt;li&gt;Blob Storage holds large, immutable payloads.&lt;/li&gt;
&lt;li&gt;CDN handles global caching.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you’ve got something fast, cheap, and resilient without pretending Blob Storage is SQL Server.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. The Verdict
&lt;/h3&gt;

&lt;p&gt;Blob Storage won’t replace your relational database. It’s not built for dynamic queries, transactions, or real-time updates.&lt;/p&gt;

&lt;p&gt;But as a &lt;strong&gt;cold-storage layer&lt;/strong&gt;, &lt;strong&gt;append-only log&lt;/strong&gt;, or &lt;strong&gt;versioned config repository&lt;/strong&gt;, it’s surprisingly elegant. And when combined with in-memory indexes or CDN caching, it can punch far above its weight.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Blob Storage isn’t dumb. It’s just misunderstood.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Used wisely, it can make your architecture simpler, cheaper, and more scalable.&lt;/p&gt;




&lt;h3&gt;
  
  
  7. Optional Extras
&lt;/h3&gt;

&lt;p&gt;If you want to take this further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Benchmark upload/download speed with &lt;code&gt;.OpenReadAsync()&lt;/code&gt; and parallel uploads.&lt;/li&gt;
&lt;li&gt;Use Azure Functions to auto-index blob metadata.&lt;/li&gt;
&lt;li&gt;Explore Blob Storage versioning for immutable event sourcing.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;I wouldn’t recommend building your startup’s core database on blobs. But if you have structured, infrequently-changing data that doesn’t need queries it might just be the &lt;strong&gt;most underrated trick&lt;/strong&gt; in Azure.&lt;/p&gt;

&lt;p&gt;What do you think? Would you ever use Blob Storage as a database?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cloud</category>
      <category>azure</category>
      <category>database</category>
    </item>
    <item>
      <title>Securely Configuring Azure Blob Storage in .NET Web API</title>
      <dc:creator>Harpreet Singh</dc:creator>
      <pubDate>Fri, 26 Sep 2025 03:33:33 +0000</pubDate>
      <link>https://dev.to/dev_harry/securely-configuring-azure-blob-storage-in-net-web-api-1gn9</link>
      <guid>https://dev.to/dev_harry/securely-configuring-azure-blob-storage-in-net-web-api-1gn9</guid>
      <description>&lt;p&gt;When you’re building a .NET Web API that integrates with Azure Blob Storage, one of the most important things to figure out is &lt;strong&gt;how you’re going to secure it&lt;/strong&gt;. Hardcoding connection strings might get you up and running quickly, but it’s definitely not something you want to do in production.&lt;/p&gt;

&lt;p&gt;The good news is that Azure gives us a few different options, from simple connection strings to more advanced setups like SAS tokens and managed identities. In this post, we’ll walk through each option and look at some real-world examples.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Getting Started – Azure Storage SDK
&lt;/h2&gt;

&lt;p&gt;First, install the Azure Storage Blob package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Azure.Storage.Blobs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, register the &lt;code&gt;BlobServiceClient&lt;/code&gt; in your Web API:&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;Azure.Storage.Blobs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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;connectionString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;  &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetConnectionString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AzureBlobStorage"&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;BlobServiceClient&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a ready-to-use &lt;code&gt;BlobServiceClient&lt;/code&gt; anywhere in your app via dependency injection.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Option 1 – Connection Strings (Quick, But Not Secure)
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;appsettings.json&lt;/code&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;"ConnectionStrings"&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;"AzureBlobStorage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DefaultEndpointsProtocol=https;AccountName=youraccount;AccountKey=yourkey;EndpointSuffix=core.windows.net"&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;This works fine for local development, but storing account keys in config is risky. If someone gets access to that file, they basically have full control of your storage account. Use it for quick demos or testing, but avoid it in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Option 2 – Shared Access Signatures (SAS Tokens)
&lt;/h2&gt;

&lt;p&gt;SAS tokens are a much safer way to give out &lt;strong&gt;temporary, scoped access&lt;/strong&gt;. Instead of exposing your account key, you can generate a token that’s valid only for a short period of time and only for specific actions (read, write, delete, etc.).&lt;/p&gt;

&lt;p&gt;Example:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;blobUri&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;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://youraccount.blob.core.windows.net/container/blob.txt?&amp;lt;SAS_TOKEN&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;blobClient&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;BlobClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blobUri&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, your Web API never deals with account keys. Instead, you just hand out short-lived tokens. Super handy for scenarios where users need to upload or download files directly.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Option 3 – Managed Identity + Azure AD (Best for Production)
&lt;/h2&gt;

&lt;p&gt;The best option in most production setups is to avoid managing secrets altogether and rely on &lt;strong&gt;Azure AD authentication&lt;/strong&gt; with &lt;strong&gt;Managed Identities&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;How it works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enable &lt;strong&gt;Managed Identity&lt;/strong&gt; for your Web API in Azure.&lt;/li&gt;
&lt;li&gt;Assign your Web API’s identity the &lt;strong&gt;Storage Blob Data Contributor&lt;/strong&gt; role in your Storage Account.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;DefaultAzureCredential&lt;/code&gt; in your app to handle authentication automatically.
&lt;/li&gt;
&lt;/ol&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;Azure.Identity&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;Azure.Storage.Blobs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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;accountUrl&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;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://youraccount.blob.core.windows.net/"&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;BlobServiceClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountUrl&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;DefaultAzureCredential&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;
  
  
  What is &lt;code&gt;DefaultAzureCredential()&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;DefaultAzureCredential&lt;/code&gt; is part of the Azure Identity library. Think of it as an authentication Swiss Army knife. It tries multiple authentication methods in order until one works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;Managed Identity (when running in Azure)&lt;/li&gt;
&lt;li&gt;Your logged-in account in Visual Studio or Azure CLI (when running locally)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means you don’t have to change your code between development and production. It “just works” in both environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Example: Uploading a File
&lt;/h2&gt;

&lt;p&gt;Here’s a simple example of uploading a file using the injected &lt;code&gt;BlobServiceClient&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/[controller]"&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;FilesController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&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;BlobServiceClient&lt;/span&gt; &lt;span class="n"&gt;_blobServiceClient&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;FilesController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BlobServiceClient&lt;/span&gt; &lt;span class="n"&gt;blobServiceClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_blobServiceClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blobServiceClient&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;HttpPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"upload"&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="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;UploadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IFormFile&lt;/span&gt; &lt;span class="n"&gt;file&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;container&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_blobServiceClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBlobContainerClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"uploads"&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;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateIfNotExistsAsync&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;blobClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBlobClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileName&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;stream&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenReadStream&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;blobClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UploadAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;overwrite&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&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;blobUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blobClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&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;This controller creates the container if it doesn’t exist, uploads the file, and returns the blob’s URL.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connection strings&lt;/strong&gt; - good for quick testing, not safe for production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SAS tokens&lt;/strong&gt; - great for temporary, scoped access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed identities + Azure AD&lt;/strong&gt; - the secure, production-ready way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DefaultAzureCredential&lt;/strong&gt; - makes local and cloud authentication seamless.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you’re designing APIs, always think about &lt;em&gt;who&lt;/em&gt; should access your storage and &lt;em&gt;for how long&lt;/em&gt;. Moving away from secrets toward managed identities and SAS tokens is the key to keeping your apps both secure and scalable.&lt;/p&gt;




&lt;p&gt;🚀 In a future post, we’ll look at &lt;strong&gt;serving files securely from Blob Storage through your Web API&lt;/strong&gt;, including download patterns and caching strategies.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cloud</category>
      <category>security</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Error Boundaries in React with TypeScript: Going Beyond the Basics</title>
      <dc:creator>Harpreet Singh</dc:creator>
      <pubDate>Wed, 24 Sep 2025 22:59:20 +0000</pubDate>
      <link>https://dev.to/dev_harry/error-boundaries-in-react-with-typescript-going-beyond-the-basics-4ffn</link>
      <guid>https://dev.to/dev_harry/error-boundaries-in-react-with-typescript-going-beyond-the-basics-4ffn</guid>
      <description>&lt;p&gt;We all know the classic React error boundary: wrap a component, catch rendering errors, and show a fallback UI. But if you’ve worked on real-world apps, you know the “textbook” approach often falls short. Async errors, route-specific crashes, and logging needs require a more advanced setup.&lt;/p&gt;

&lt;p&gt;In this article, I’ll walk you through a &lt;strong&gt;TypeScript-first approach to error boundaries&lt;/strong&gt; that makes your React apps more resilient, easier to debug, and more user-friendly.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Strongly Typed Error Boundary
&lt;/h2&gt;

&lt;p&gt;First, let’s define a solid, TypeScript-friendly error boundary that handles errors gracefully and logs them:&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ErrorBoundaryProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;children&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;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;fallback&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;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ErrorBoundaryState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;hasError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppErrorBoundary&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ErrorBoundaryProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ErrorBoundaryState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ErrorBoundaryState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hasError&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="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;getDerivedStateFromError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ErrorBoundaryState&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="na"&gt;hasError&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="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="nf"&gt;componentDidCatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;info&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;ErrorInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Log to monitoring service&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Logged Error:&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;info&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;if &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;hasError&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;this&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="nx"&gt;fallback&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Something&lt;/span&gt; &lt;span class="nx"&gt;went&lt;/span&gt; &lt;span class="nx"&gt;wrong&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&amp;gt;&lt;/span&gt;&lt;span class="err"&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;this&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="nx"&gt;children&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;This gives you &lt;strong&gt;type safety&lt;/strong&gt; for both props and state while keeping your error handling centralized.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Route-Level Boundaries: Isolate the Crashes
&lt;/h2&gt;

&lt;p&gt;Instead of one giant boundary at the root of your app, wrap &lt;strong&gt;specific routes or features&lt;/strong&gt;. This way, a single failing page doesn’t crash your whole app:&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppErrorBoundary&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;./AppErrorBoundary&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;Dashboard&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;./Dashboard&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;DashboardRoute&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;AppErrorBoundary&lt;/span&gt; &lt;span class="na"&gt;fallback&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;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Dashboard failed to load.&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Dashboard&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;AppErrorBoundary&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users can still navigate your app, even if one page has an error.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Handling Async and Event Errors
&lt;/h2&gt;

&lt;p&gt;React error boundaries don’t catch errors in &lt;code&gt;async/await&lt;/code&gt; or event handlers. To fix this, wrap your async functions:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;safeAsync&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="nf"&gt;extends &lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&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;async &lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Parameters&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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="nb"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;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="k"&gt;try&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;await&lt;/span&gt; &lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&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="nx"&gt;err&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Async error:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// optional: let ErrorBoundary catch it if needed&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="c1"&gt;// Usage&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;safeAsync&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Boom!&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;&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;handleClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Click Me&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures async crashes are logged and can be optionally caught by your boundary.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Resettable Boundaries: Let Users Recover
&lt;/h2&gt;

&lt;p&gt;A frozen fallback UI is frustrating. With &lt;code&gt;react-error-boundary&lt;/code&gt;, you can provide a &lt;strong&gt;retry button&lt;/strong&gt;:&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ErrorBoundary&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-error-boundary&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;Fallback&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="nx"&gt;resetErrorBoundary&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;resetErrorBoundary&lt;/span&gt;&lt;span class="p"&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="k"&gt;void&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;(&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;Something went wrong: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&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="nx"&gt;resetErrorBoundary&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Try Again&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ErrorBoundary&lt;/span&gt;
  &lt;span class="na"&gt;FallbackComponent&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Fallback&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;onError&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;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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Caught by boundary:&lt;/span&gt;&lt;span class="dl"&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="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;resetKeys&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="cm"&gt;/* state/props that trigger reset */&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Dashboard&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;ErrorBoundary&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users can recover without having to refresh the page.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Layered Approach for Production-Ready Apps
&lt;/h2&gt;

&lt;p&gt;Combine these strategies for a robust setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Global boundary&lt;/strong&gt; - catches catastrophic failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route/component boundaries&lt;/strong&gt; - isolate crashes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Async wrappers + logging&lt;/strong&gt; - capture what React misses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resettable fallbacks&lt;/strong&gt; - improve user experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This layered approach keeps your app resilient and your users happy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;React’s built-in error boundaries are just the starting point. In real apps, you need a &lt;strong&gt;TypeScript-first, layered strategy&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strong typing for safety&lt;/li&gt;
&lt;li&gt;Logging for observability&lt;/li&gt;
&lt;li&gt;Isolation for reliability&lt;/li&gt;
&lt;li&gt;Recovery for UX&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way, errors are no longer showstoppers, they’re just part of a manageable system.&lt;/p&gt;




&lt;p&gt;If you enjoyed this, check out my other articles for more &lt;strong&gt;advanced, production-ready patterns&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>frontend</category>
      <category>development</category>
    </item>
    <item>
      <title>Managing API Version Transitions Across Teams</title>
      <dc:creator>Harpreet Singh</dc:creator>
      <pubDate>Wed, 24 Sep 2025 03:24:37 +0000</pubDate>
      <link>https://dev.to/dev_harry/managing-api-version-transitions-across-teams-5ecl</link>
      <guid>https://dev.to/dev_harry/managing-api-version-transitions-across-teams-5ecl</guid>
      <description>&lt;p&gt;API versioning solves the breaking changes problem, but in practice, &lt;strong&gt;coordination between backend and frontend teams&lt;/strong&gt; is often where the real pain happens. Deploying a new version too soon can leave frontend devs scratching their heads wondering why types don’t match or why fields disappeared.&lt;/p&gt;

&lt;p&gt;Here are strategies (inspired by real-world team war stories) to make version transitions smoother:&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Run Versions in Parallel
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Keep &lt;strong&gt;v1 live as “stable”&lt;/strong&gt; (even if marked as deprecated).&lt;/li&gt;
&lt;li&gt;Deploy &lt;strong&gt;v2 alongside it&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Give frontend teams a clear migration window (e.g. 60–90 days).&lt;/li&gt;
&lt;li&gt;Avoid hard cutovers unless the app is internal or you control all clients.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ This buys teams breathing room instead of rushing migrations.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Contracts with OpenAPI Specs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Treat your &lt;strong&gt;OpenAPI spec as a contract&lt;/strong&gt; between backend and frontend.&lt;/li&gt;
&lt;li&gt;Diff spec files between versions to see exactly what changed.&lt;/li&gt;
&lt;li&gt;Share updated spec files as part of your release notes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why this matters
&lt;/h3&gt;

&lt;p&gt;OpenAPI specs are a &lt;strong&gt;single source of truth&lt;/strong&gt;. Backend teams define what the API should look like, while frontend teams consume it without relying on guesswork or Slack messages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Diffing versions
&lt;/h3&gt;

&lt;p&gt;You can literally &lt;em&gt;diff&lt;/em&gt; spec files between versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openapi-diff old-spec.yaml new-spec.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Endpoints that stayed the same&lt;/li&gt;
&lt;li&gt;⚠️ Fields that changed type (e.g., &lt;code&gt;int&lt;/code&gt; → &lt;code&gt;string&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;❌ Endpoints that were removed (breaking change)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Sharing specs in releases
&lt;/h3&gt;

&lt;p&gt;Include spec files alongside your release notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;openapi-v1.yaml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;openapi-v2.yaml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Frontend teams can generate clients or update mocks immediately, even before the backend deploys.&lt;/p&gt;

&lt;h3&gt;
  
  
  Living documentation
&lt;/h3&gt;

&lt;p&gt;Host multiple Swagger/OpenAPI docs so consumers always know what’s live:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/swagger/v1/swagger.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/swagger/v2/swagger.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes it crystal clear which version is in use and prevents "I &lt;em&gt;think&lt;/em&gt; this field changed?" confusion.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Mock Servers for Multiple Versions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Spin up &lt;strong&gt;mock servers&lt;/strong&gt; from OpenAPI specs.&lt;/li&gt;
&lt;li&gt;Frontend teams can test against v1 and v2 simultaneously.&lt;/li&gt;
&lt;li&gt;Migration code can be written &lt;em&gt;before&lt;/em&gt; the backend is even live.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ You catch breaking changes early, not during frantic late-night debugging.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. TypeScript (or Client) Code Generation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Generate &lt;strong&gt;version-specific TypeScript clients&lt;/strong&gt; from OpenAPI specs.&lt;/li&gt;
&lt;li&gt;Compile-time errors show exactly what changed.&lt;/li&gt;
&lt;li&gt;Keep &lt;strong&gt;separate packages/namespaces&lt;/strong&gt; for v1 and v2 to avoid confusion.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ Prevents 3-hour debugging sessions where types don’t match the API anymore.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Deprecation Flags = Communication
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Mark older versions as &lt;strong&gt;deprecated&lt;/strong&gt; using attributes in code.&lt;/li&gt;
&lt;li&gt;Return deprecation warnings in response headers.&lt;/li&gt;
&lt;li&gt;Document sunset timelines in your API docs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ Gives frontend teams time to plan migrations instead of dealing with surprises.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Smooth version transitions are as much about &lt;strong&gt;team coordination&lt;/strong&gt; as they are about code. By running versions in parallel, leaning on OpenAPI specs, using mock servers, and generating client types, you can save both backend and frontend teams a ton of headaches.&lt;/p&gt;

&lt;p&gt;The rule of thumb? &lt;strong&gt;Don’t surprise your clients.&lt;/strong&gt; Version transitions should feel predictable, not like debugging a ghost API.&lt;/p&gt;




&lt;p&gt;✍️ If you enjoyed this, check out my previous article on &lt;a href="https://dev.to/harpreet_singh_c4ea4af253/api-versioning-in-net-web-api-39ma"&gt;API Versioning in .NET Web API&lt;/a&gt; for a hands-on implementation guide.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>openapi</category>
      <category>productivity</category>
      <category>api</category>
    </item>
    <item>
      <title>Implementing Caching in .NET Core Web API</title>
      <dc:creator>Harpreet Singh</dc:creator>
      <pubDate>Tue, 23 Sep 2025 23:35:20 +0000</pubDate>
      <link>https://dev.to/dev_harry/implementing-caching-in-net-core-web-api-4d6l</link>
      <guid>https://dev.to/dev_harry/implementing-caching-in-net-core-web-api-4d6l</guid>
      <description>&lt;p&gt;Caching is one of the most effective ways to improve the performance and scalability of your APIs. By storing frequently requested data, you can reduce database load, speed up response times, and handle higher traffic without degrading performance.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore &lt;strong&gt;memory caching&lt;/strong&gt;, &lt;strong&gt;response caching&lt;/strong&gt;, and &lt;strong&gt;distributed caching&lt;/strong&gt; in .NET Core Web API with practical examples.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Response Caching
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Response caching&lt;/strong&gt; allows responses from your API to be stored and reused for a specified duration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup in &lt;code&gt;Program.cs&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddResponseCaching&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;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseResponseCaching&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using Response Caching in Controller
&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="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/[controller]"&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;ProductsController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ResponseCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Duration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ResponseCacheLocation&lt;/span&gt;&lt;span class="p"&gt;.&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;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Get&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;products&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="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Product 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Product 2"&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;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;products&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;✅ The API response will be cached for &lt;strong&gt;60 seconds&lt;/strong&gt; on the client.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. In-Memory Caching
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;In-memory caching&lt;/strong&gt; stores data in the server’s memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup in &lt;code&gt;Program.cs&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddMemoryCache&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using In-Memory Cache in Controller
&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="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/[controller]"&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;OrdersController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&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;IMemoryCache&lt;/span&gt; &lt;span class="n"&gt;_cache&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;OrdersController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IMemoryCache&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_cache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&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="n"&gt;HttpGet&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;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;GetOrders&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cacheKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"orders_list"&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;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&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;orders&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Simulate fetching from database&lt;/span&gt;
            &lt;span class="n"&gt;orders&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="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Order 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Order 2"&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;cacheOptions&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;MemoryCacheEntryOptions&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;AbsoluteExpirationRelativeToNow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&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="n"&gt;SlidingExpiration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;

            &lt;span class="n"&gt;_cache&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="n"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cacheOptions&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="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orders&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;✅ Reduces repeated database calls for frequently accessed data.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Distributed Caching
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Distributed caching&lt;/strong&gt; stores cache outside of the application, suitable for &lt;strong&gt;multi-server environments&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup with Redis
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddStackExchangeRedisCache&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;=&amp;gt;&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="n"&gt;Configuration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"localhost:6379"&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="n"&gt;InstanceName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"MyApp_"&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;
  
  
  Using Distributed Cache in Controller
&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="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/[controller]"&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;CustomersController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&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;IDistributedCache&lt;/span&gt; &lt;span class="n"&gt;_cache&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;CustomersController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IDistributedCache&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_cache&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&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="n"&gt;HttpGet&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="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;GetCustomers&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;cacheKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"customers_list"&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;cachedData&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;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheKey&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;cachedData&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;customers&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="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Customer 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Customer 2"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="n"&gt;cachedData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&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;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;customers&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;options&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;DistributedCacheEntryOptions&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;AbsoluteExpirationRelativeToNow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromMinutes&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="p"&gt;};&lt;/span&gt;

            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetStringAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cachedData&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&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;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="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;]&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cachedData&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;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&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;✅ Works across multiple instances of your API in a load-balanced environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Caching is essential for improving &lt;strong&gt;API performance, scalability, and user experience&lt;/strong&gt;. In .NET Core Web API, you have several options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Response Caching:&lt;/strong&gt; For simple, client-side caching of responses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In-Memory Caching:&lt;/strong&gt; Quick caching within a single server instance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributed Caching:&lt;/strong&gt; Scalable caching across multiple servers (e.g., Redis).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By implementing caching appropriately, you can drastically reduce database load and improve response times for your users.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>dotnetcore</category>
      <category>csharp</category>
    </item>
    <item>
      <title>API Versioning in .NET Web API</title>
      <dc:creator>Harpreet Singh</dc:creator>
      <pubDate>Tue, 23 Sep 2025 23:06:43 +0000</pubDate>
      <link>https://dev.to/dev_harry/api-versioning-in-net-web-api-39ma</link>
      <guid>https://dev.to/dev_harry/api-versioning-in-net-web-api-39ma</guid>
      <description>&lt;p&gt;As your API evolves, breaking changes are inevitable. Introducing &lt;strong&gt;API versioning&lt;/strong&gt; ensures that your clients continue to work without disruption while you introduce new features and improvements.&lt;/p&gt;

&lt;p&gt;This guide covers how to implement API versioning in .NET Web API with practical examples, including a clean &lt;strong&gt;BaseApiController&lt;/strong&gt; approach for centralized configuration.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why API Versioning?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backward Compatibility:&lt;/strong&gt; Old clients continue to work with previous versions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smooth Upgrades:&lt;/strong&gt; You can introduce new endpoints without breaking existing functionality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clear Communication:&lt;/strong&gt; Clients know which version they are using.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better Lifecycle Management:&lt;/strong&gt; Easier to deprecate old versions gradually.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Add Required NuGet Package
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Microsoft.AspNetCore.Mvc.Versioning
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This package provides tools to version your API by query string, header, or URL segment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Configure Versioning in &lt;code&gt;Program.cs&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Add API versioning&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddApiVersioning&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;=&amp;gt;&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="n"&gt;AssumeDefaultVersionWhenUnspecified&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;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultApiVersion&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;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AspNetCore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mvc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ApiVersion&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;0&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="n"&gt;ReportApiVersions&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="c1"&gt;// Returns API versions in response headers&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;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Create a BaseApiController
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;BaseApiController&lt;/strong&gt; can hold common configuration, attributes, and helper methods for all versioned controllers.&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;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/v{version:apiVersion}/[controller]"&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;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BaseApiController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;ApiResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;statusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;200&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="nf"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statusCode&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;Status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetRequestedApiVersion&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nf"&gt;ToString&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;All your versioned controllers can now &lt;strong&gt;inherit&lt;/strong&gt; from this base controller for consistent behavior.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Versioning Controllers Using BaseApiController
&lt;/h2&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;ApiVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1.0"&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;ProductsController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BaseApiController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&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;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ApiResponse&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="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Product 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Product 2"&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;ApiVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2.0"&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;ProductsV2Controller&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BaseApiController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&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;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ApiResponse&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="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Product A"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Product B"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Product C"&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;This keeps your code &lt;strong&gt;DRY&lt;/strong&gt; and centralizes versioning logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Query String and Header-Based Versioning
&lt;/h2&gt;

&lt;p&gt;Query string example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/orders?api-version=1.0
GET /api/orders?api-version=2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Header-based configuration in &lt;code&gt;Program.cs&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="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddApiVersioning&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;=&amp;gt;&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="n"&gt;AssumeDefaultVersionWhenUnspecified&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;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultApiVersion&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;ApiVersion&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;0&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="n"&gt;ApiVersionReader&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;HeaderApiVersionReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-api-version"&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="n"&gt;ReportApiVersions&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clients can now set the header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x-api-version: 2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Deprecating Versions
&lt;/h2&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;ApiVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Deprecated&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OldController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BaseApiController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&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;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ApiResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This version is deprecated"&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;Headers will indicate deprecation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Swagger Integration for Versioned APIs
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSwaggerGen&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;=&amp;gt;&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="nf"&gt;SwaggerDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"v1"&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;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenApiInfo&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="s"&gt;"API v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"v1"&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="nf"&gt;SwaggerDoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"v2"&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;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenApiInfo&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="s"&gt;"API v2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"v2"&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;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSwagger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSwaggerUI&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;=&amp;gt;&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="nf"&gt;SwaggerEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/swagger/v1/swagger.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"API v1"&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="nf"&gt;SwaggerEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/swagger/v2/swagger.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"API v2"&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;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Using a &lt;strong&gt;BaseApiController&lt;/strong&gt; centralizes common logic, keeps controllers DRY, and makes managing API versioning cleaner. Combined with URL, query string, or header-based versioning and Swagger integration, this approach provides a &lt;strong&gt;flexible and maintainable API structure&lt;/strong&gt; in .NET.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>api</category>
      <category>dotnet</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Global Exception Handling in .NET Web API</title>
      <dc:creator>Harpreet Singh</dc:creator>
      <pubDate>Tue, 23 Sep 2025 04:50:07 +0000</pubDate>
      <link>https://dev.to/dev_harry/global-exception-handling-in-net-web-api-3aln</link>
      <guid>https://dev.to/dev_harry/global-exception-handling-in-net-web-api-3aln</guid>
      <description>&lt;p&gt;When building APIs, one of the most common mistakes developers make is &lt;strong&gt;scattering try-catch blocks across controllers&lt;/strong&gt;. Not only does this make the code messy, but it also leads to inconsistent error responses for clients.&lt;/p&gt;

&lt;p&gt;The solution? &lt;strong&gt;Global Exception Handling&lt;/strong&gt; using middleware in .NET Web API.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Global Exception Handling?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Centralized error management&lt;/strong&gt;: All exceptions go through one pipeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent responses&lt;/strong&gt;: Clients always receive structured error messages.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleaner controllers&lt;/strong&gt;: No need for repetitive try-catch blocks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better logging&lt;/strong&gt;: Easier to log all unhandled exceptions.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Create Exception Handling Middleware
&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;class&lt;/span&gt; &lt;span class="nc"&gt;GlobalExceptionMiddleware&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;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;_next&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;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GlobalExceptionMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&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;GlobalExceptionMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestDelegate&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GlobalExceptionMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_next&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;_next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&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;_logger&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="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Unhandled exception occurred."&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;HandleExceptionAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&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="p"&gt;}&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="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;HandleExceptionAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="n"&gt;context&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;exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&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;ContentType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&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;StatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&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;HttpStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InternalServerError&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;new&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;StatusCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&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;StatusCode&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;"Internal Server Error. Please try again later."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Detail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="c1"&gt;// Optional: remove in production&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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteAsJsonAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&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;
  
  
  Step 2: Register Middleware in &lt;code&gt;Program.cs&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&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;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Add global exception middleware&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GlobalExceptionMiddleware&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Testing the Middleware
&lt;/h2&gt;

&lt;p&gt;Add a controller that throws an exception:&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/[controller]"&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;TestController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"error"&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;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;GetError&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidOperationException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"This is a test exception."&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;Call &lt;code&gt;GET /api/test/error&lt;/code&gt; and you should get a JSON response like:&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;"StatusCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Internal Server Error. Please try again later."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This is a test exception."&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;h2&gt;
  
  
  Step 4: Optional Enhancements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Environment-aware messages&lt;/strong&gt;: Hide &lt;code&gt;Detail&lt;/code&gt; in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom exception types&lt;/strong&gt;: Handle validation exceptions differently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured logging&lt;/strong&gt;: Include correlation IDs for tracing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Problem Details RFC 7807&lt;/strong&gt;: Return standardized error responses.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Global exception handling keeps your Web API &lt;strong&gt;clean, consistent, and easier to maintain&lt;/strong&gt;. By centralizing error management, you free controllers from repetitive try-catch blocks, make logging easier, and ensure clients always receive a predictable response.&lt;/p&gt;

&lt;p&gt;This pattern is a must-have for any production-ready .NET Web API.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>backend</category>
      <category>dotnet</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
