<?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: Jeremiah Deku</title>
    <description>The latest articles on DEV Community by Jeremiah Deku (@jeremiah_dek).</description>
    <link>https://dev.to/jeremiah_dek</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%2F1716007%2Ff01aee24-9f43-4014-9c0f-3ffafdbaf7e9.png</url>
      <title>DEV Community: Jeremiah Deku</title>
      <link>https://dev.to/jeremiah_dek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jeremiah_dek"/>
    <language>en</language>
    <item>
      <title>Check this out</title>
      <dc:creator>Jeremiah Deku</dc:creator>
      <pubDate>Thu, 23 Apr 2026 01:11:04 +0000</pubDate>
      <link>https://dev.to/jeremiah_dek/check-this-out-46l9</link>
      <guid>https://dev.to/jeremiah_dek/check-this-out-46l9</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/jeremiah_dek/how-i-fixed-a-race-condition-in-a-live-seat-booking-system-and-lost-sleep-over-it-48ii" class="crayons-story__hidden-navigation-link"&gt;How I Fixed a Race Condition in a Live Seat Booking System (And Lost Sleep Over It)&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/jeremiah_dek" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1716007%2Ff01aee24-9f43-4014-9c0f-3ffafdbaf7e9.png" alt="jeremiah_dek profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/jeremiah_dek" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Jeremiah Deku
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Jeremiah Deku
                
              
              &lt;div id="story-author-preview-content-3422700" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/jeremiah_dek" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1716007%2Ff01aee24-9f43-4014-9c0f-3ffafdbaf7e9.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Jeremiah Deku&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/jeremiah_dek/how-i-fixed-a-race-condition-in-a-live-seat-booking-system-and-lost-sleep-over-it-48ii" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 29&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/jeremiah_dek/how-i-fixed-a-race-condition-in-a-live-seat-booking-system-and-lost-sleep-over-it-48ii" id="article-link-3422700"&gt;
          How I Fixed a Race Condition in a Live Seat Booking System (And Lost Sleep Over It)
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/notionchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;notionchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/jeremiah_dek/how-i-fixed-a-race-condition-in-a-live-seat-booking-system-and-lost-sleep-over-it-48ii" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;8&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/jeremiah_dek/how-i-fixed-a-race-condition-in-a-live-seat-booking-system-and-lost-sleep-over-it-48ii#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              2&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>The exact error handling pattern I use in every Express.js + TypeScript REST API</title>
      <dc:creator>Jeremiah Deku</dc:creator>
      <pubDate>Wed, 22 Apr 2026 18:04:07 +0000</pubDate>
      <link>https://dev.to/jeremiah_dek/the-exact-error-handling-pattern-i-use-in-every-expressjs-typescript-rest-api-4fg8</link>
      <guid>https://dev.to/jeremiah_dek/the-exact-error-handling-pattern-i-use-in-every-expressjs-typescript-rest-api-4fg8</guid>
      <description>&lt;p&gt;Most Node.js error handling tutorials show you try/catch.&lt;br&gt;
That's not enough for production.&lt;br&gt;
I learned this the hard way. A silent catch block swallowed a database failure. My API returned 200. The user's data never saved. Nobody knew for 48 hours.&lt;br&gt;
After that incident, I built a pattern I now wire into every Express + TypeScript REST API I ship. It has four parts. Every part earns its place.&lt;/p&gt;

&lt;p&gt;Here's exactly what it looks like — and why each piece matters.&lt;/p&gt;




&lt;p&gt;𝗧𝗵𝗲 𝗽𝗿𝗼𝗯𝗹𝗲𝗺 𝘄𝗶𝘁𝗵 𝗱𝗲𝗳𝗮𝘂𝗹𝘁 𝗘𝘅𝗽𝗿𝗲𝘀𝘀 𝗲𝗿𝗿𝗼𝗿 𝗵𝗮𝗻𝗱𝗹𝗶𝗻𝗴&lt;br&gt;
Out of the box, Express does this when something throws:&lt;/p&gt;

&lt;p&gt;𝘢𝘱𝘱.𝘨𝘦𝘵('/𝘶𝘴𝘦𝘳𝘴/:𝘪𝘥', 𝘢𝘴𝘺𝘯𝘤 (𝘳𝘦𝘲, 𝘳𝘦𝘴) =&amp;gt; {&lt;br&gt;
  𝘤𝘰𝘯𝘴𝘵 𝘶𝘴𝘦𝘳 = 𝘢𝘸𝘢𝘪𝘵 𝘥𝘣.𝘧𝘪𝘯𝘥𝘉𝘺𝘐𝘥(𝘳𝘦𝘲.𝘱𝘢𝘳𝘢𝘮𝘴.𝘪𝘥); // 𝘵𝘩𝘳𝘰𝘸𝘴? 𝘤𝘳𝘢𝘴𝘩𝘦𝘴 𝘵𝘩𝘦 𝘱𝘳𝘰𝘤𝘦𝘴𝘴&lt;br&gt;
  𝘳𝘦𝘴.𝘫𝘴𝘰𝘯(𝘶𝘴𝘦𝘳);&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;If db.findById throws, Express doesn't catch it in async route handlers by default. Your server either crashes or hangs. The client waits forever.&lt;br&gt;
Even if you add try/catch:&lt;br&gt;
𝘢𝘱𝘱.𝘨𝘦𝘵('/𝘶𝘴𝘦𝘳𝘴/:𝘪𝘥', 𝘢𝘴𝘺𝘯𝘤 (𝘳𝘦𝘲, 𝘳𝘦𝘴) =&amp;gt; {&lt;br&gt;
  𝘵𝘳𝘺 {&lt;br&gt;
    𝘤𝘰𝘯𝘴𝘵 𝘶𝘴𝘦𝘳 = 𝘢𝘸𝘢𝘪𝘵 𝘥𝘣.𝘧𝘪𝘯𝘥𝘉𝘺𝘐𝘥(𝘳𝘦𝘲.𝘱𝘢𝘳𝘢𝘮𝘴.𝘪𝘥);&lt;br&gt;
    𝘳𝘦𝘴.𝘫𝘴𝘰𝘯(𝘶𝘴𝘦𝘳);&lt;br&gt;
  } 𝘤𝘢𝘵𝘤𝘩 (𝘦𝘳𝘳) {&lt;br&gt;
    𝘳𝘦𝘴.𝘴𝘵𝘢𝘵𝘶𝘴(500).𝘫𝘴𝘰𝘯({ 𝘦𝘳𝘳𝘰𝘳: 𝘦𝘳𝘳.𝘮𝘦𝘴𝘴𝘢𝘨𝘦 }); // 𝘭𝘦𝘢𝘬𝘴 𝘪𝘯𝘵𝘦𝘳𝘯𝘢𝘭𝘴&lt;br&gt;
  }&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;Now you're leaking internal error messages to clients, losing stack traces in logs, and duplicating error logic across every route.&lt;br&gt;
Here's what I do instead.&lt;/p&gt;




&lt;p&gt;𝗣𝗮𝗿𝘁 𝟭: 𝗔 𝘁𝘆𝗽𝗲𝗱 𝗔𝗽𝗽𝗘𝗿𝗿𝗼𝗿 𝗰𝗹𝗮𝘀𝘀&lt;br&gt;
Every error in the system extends a single base class. No raw new Error() anywhere in the codebase.&lt;br&gt;
// 𝘴𝘳𝘤/𝘦𝘳𝘳𝘰𝘳𝘴/𝘈𝘱𝘱𝘌𝘳𝘳𝘰𝘳.𝘵𝘴&lt;/p&gt;

&lt;p&gt;𝘦𝘹𝘱𝘰𝘳𝘵 𝘤𝘭𝘢𝘴𝘴 𝘈𝘱𝘱𝘌𝘳𝘳𝘰𝘳 𝘦𝘹𝘵𝘦𝘯𝘥𝘴 𝘌𝘳𝘳𝘰𝘳 {&lt;br&gt;
  𝘤𝘰𝘯𝘴𝘵𝘳𝘶𝘤𝘵𝘰𝘳(&lt;br&gt;
    𝘱𝘶𝘣𝘭𝘪𝘤 𝘳𝘦𝘢𝘥𝘰𝘯𝘭𝘺 𝘮𝘦𝘴𝘴𝘢𝘨𝘦: 𝘴𝘵𝘳𝘪𝘯𝘨,&lt;br&gt;
    𝘱𝘶𝘣𝘭𝘪𝘤 𝘳𝘦𝘢𝘥𝘰𝘯𝘭𝘺 𝘴𝘵𝘢𝘵𝘶𝘴𝘊𝘰𝘥𝘦: 𝘯𝘶𝘮𝘣𝘦𝘳,&lt;br&gt;
    𝘱𝘶𝘣𝘭𝘪𝘤 𝘳𝘦𝘢𝘥𝘰𝘯𝘭𝘺 𝘤𝘰𝘥𝘦: 𝘴𝘵𝘳𝘪𝘯𝘨,&lt;br&gt;
    𝘱𝘶𝘣𝘭𝘪𝘤 𝘳𝘦𝘢𝘥𝘰𝘯𝘭𝘺 𝘪𝘴𝘖𝘱𝘦𝘳𝘢𝘵𝘪𝘰𝘯𝘢𝘭: 𝘣𝘰𝘰𝘭𝘦𝘢𝘯 = 𝘵𝘳𝘶𝘦&lt;br&gt;
  ) {&lt;br&gt;
    𝘴𝘶𝘱𝘦𝘳(𝘮𝘦𝘴𝘴𝘢𝘨𝘦);&lt;br&gt;
    𝘵𝘩𝘪𝘴.𝘯𝘢𝘮𝘦 = 𝘵𝘩𝘪𝘴.𝘤𝘰𝘯𝘴𝘵𝘳𝘶𝘤𝘵𝘰𝘳.𝘯𝘢𝘮𝘦;&lt;br&gt;
    𝘌𝘳𝘳𝘰𝘳.𝘤𝘢𝘱𝘵𝘶𝘳𝘦𝘚𝘵𝘢𝘤𝘬𝘛𝘳𝘢𝘤𝘦(𝘵𝘩𝘪𝘴, 𝘵𝘩𝘪𝘴.𝘤𝘰𝘯𝘴𝘵𝘳𝘶𝘤𝘵𝘰𝘳);&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;𝘦𝘹𝘱𝘰𝘳𝘵 𝘤𝘭𝘢𝘴𝘴 𝘕𝘰𝘵𝘍𝘰𝘶𝘯𝘥𝘌𝘳𝘳𝘰𝘳 𝘦𝘹𝘵𝘦𝘯𝘥𝘴 𝘈𝘱𝘱𝘌𝘳𝘳𝘰𝘳 {&lt;br&gt;
  𝘤𝘰𝘯𝘴𝘵𝘳𝘶𝘤𝘵𝘰𝘳(𝘳𝘦𝘴𝘰𝘶𝘳𝘤𝘦: 𝘴𝘵𝘳𝘪𝘯𝘨) {&lt;br&gt;
    𝘴𝘶𝘱𝘦𝘳(&lt;code&gt;${𝘳𝘦𝘴𝘰𝘶𝘳𝘤𝘦} 𝘯𝘰𝘵 𝘧𝘰𝘶𝘯𝘥&lt;/code&gt;, 404, '𝘕𝘖𝘛_𝘍𝘖𝘜𝘕𝘋');&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;𝘦𝘹𝘱𝘰𝘳𝘵 𝘤𝘭𝘢𝘴𝘴 𝘝𝘢𝘭𝘪𝘥𝘢𝘵𝘪𝘰𝘯𝘌𝘳𝘳𝘰𝘳 𝘦𝘹𝘵𝘦𝘯𝘥𝘴 𝘈𝘱𝘱𝘌𝘳𝘳𝘰𝘳 {&lt;br&gt;
  𝘤𝘰𝘯𝘴𝘵𝘳𝘶𝘤𝘵𝘰𝘳(𝘮𝘦𝘴𝘴𝘢𝘨𝘦: 𝘴𝘵𝘳𝘪𝘯𝘨) {&lt;br&gt;
    𝘴𝘶𝘱𝘦𝘳(𝘮𝘦𝘴𝘴𝘢𝘨𝘦, 422, '𝘝𝘈𝘓𝘐𝘋𝘈𝘛𝘐𝘖𝘕_𝘌𝘙𝘙𝘖𝘙');&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;𝘦𝘹𝘱𝘰𝘳𝘵 𝘤𝘭𝘢𝘴𝘴 𝘜𝘯𝘢𝘶𝘵𝘩𝘰𝘳𝘪𝘻𝘦𝘥𝘌𝘳𝘳𝘰𝘳 𝘦𝘹𝘵𝘦𝘯𝘥𝘴 𝘈𝘱𝘱𝘌𝘳𝘳𝘰𝘳 {&lt;br&gt;
  𝘤𝘰𝘯𝘴𝘵𝘳𝘶𝘤𝘵𝘰𝘳() {&lt;br&gt;
    𝘴𝘶𝘱𝘦𝘳('𝘜𝘯𝘢𝘶𝘵𝘩𝘰𝘳𝘪𝘻𝘦𝘥', 401, '𝘜𝘕𝘈𝘜𝘛𝘏𝘖𝘙𝘐𝘡𝘌𝘋');&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;isOperational is the key flag. true means this is an expected failure — a user not found, a bad payload. false means something unexpected broke — a DB connection dropped, a third-party SDK crashed. Your error handler treats these differently.&lt;/p&gt;




&lt;p&gt;𝗣𝗮𝗿𝘁 𝟮: 𝗔 𝗰𝗲𝗻𝘁𝗿𝗮𝗹 𝗲𝗿𝗿𝗼𝗿 𝗵𝗮𝗻𝗱𝗹𝗲𝗿 𝗺𝗶𝗱𝗱𝗹𝗲𝘄𝗮𝗿𝗲&lt;/p&gt;

&lt;p&gt;// 𝘴𝘳𝘤/𝘮𝘪𝘥𝘥𝘭𝘦𝘸𝘢𝘳𝘦/𝘦𝘳𝘳𝘰𝘳𝘏𝘢𝘯𝘥𝘭𝘦𝘳.𝘵𝘴&lt;br&gt;
𝘪𝘮𝘱𝘰𝘳𝘵 { 𝘙𝘦𝘲𝘶𝘦𝘴𝘵, 𝘙𝘦𝘴𝘱𝘰𝘯𝘴𝘦, 𝘕𝘦𝘹𝘵𝘍𝘶𝘯𝘤𝘵𝘪𝘰𝘯 } 𝘧𝘳𝘰𝘮 '𝘦𝘹𝘱𝘳𝘦𝘴𝘴';&lt;br&gt;
𝘪𝘮𝘱𝘰𝘳𝘵 { 𝘈𝘱𝘱𝘌𝘳𝘳𝘰𝘳 } 𝘧𝘳𝘰𝘮 '../𝘦𝘳𝘳𝘰𝘳𝘴/𝘈𝘱𝘱𝘌𝘳𝘳𝘰𝘳';&lt;br&gt;
𝘪𝘮𝘱𝘰𝘳𝘵 { 𝘭𝘰𝘨𝘨𝘦𝘳 } 𝘧𝘳𝘰𝘮 '../𝘭𝘪𝘣/𝘭𝘰𝘨𝘨𝘦𝘳';&lt;/p&gt;

&lt;p&gt;𝘦𝘹𝘱𝘰𝘳𝘵 𝘧𝘶𝘯𝘤𝘵𝘪𝘰𝘯 𝘦𝘳𝘳𝘰𝘳𝘏𝘢𝘯𝘥𝘭𝘦𝘳(&lt;br&gt;
  𝘦𝘳𝘳: 𝘌𝘳𝘳𝘰𝘳,&lt;br&gt;
  𝘳𝘦𝘲: 𝘙𝘦𝘲𝘶𝘦𝘴𝘵,&lt;br&gt;
  𝘳𝘦𝘴: 𝘙𝘦𝘴𝘱𝘰𝘯𝘴𝘦,&lt;br&gt;
  𝘯𝘦𝘹𝘵: 𝘕𝘦𝘹𝘵𝘍𝘶𝘯𝘤𝘵𝘪𝘰𝘯&lt;br&gt;
): 𝘷𝘰𝘪𝘥 {&lt;br&gt;
  𝘤𝘰𝘯𝘴𝘵 𝘳𝘦𝘲𝘶𝘦𝘴𝘵𝘐𝘥 = 𝘳𝘦𝘲.𝘩𝘦𝘢𝘥𝘦𝘳𝘴['𝘹-𝘳𝘦𝘲𝘶𝘦𝘴𝘵-𝘪𝘥'] 𝘢𝘴 𝘴𝘵𝘳𝘪𝘯𝘨;&lt;/p&gt;

&lt;p&gt;𝘪𝘧 (𝘦𝘳𝘳 𝘪𝘯𝘴𝘵𝘢𝘯𝘤𝘦𝘰𝘧 𝘈𝘱𝘱𝘌𝘳𝘳𝘰𝘳 &amp;amp;&amp;amp; 𝘦𝘳𝘳.𝘪𝘴𝘖𝘱𝘦𝘳𝘢𝘵𝘪𝘰𝘯𝘢𝘭) {&lt;br&gt;
    // 𝘌𝘹𝘱𝘦𝘤𝘵𝘦𝘥 𝘦𝘳𝘳𝘰𝘳 — 𝘭𝘰𝘨 𝘭𝘪𝘨𝘩𝘵𝘭𝘺, 𝘳𝘦𝘴𝘱𝘰𝘯𝘥 𝘤𝘭𝘦𝘢𝘯𝘭𝘺&lt;br&gt;
    𝘭𝘰𝘨𝘨𝘦𝘳.𝘸𝘢𝘳𝘯({&lt;br&gt;
      𝘳𝘦𝘲𝘶𝘦𝘴𝘵𝘐𝘥,&lt;br&gt;
      𝘤𝘰𝘥𝘦: 𝘦𝘳𝘳.𝘤𝘰𝘥𝘦,&lt;br&gt;
      𝘮𝘦𝘴𝘴𝘢𝘨𝘦: 𝘦𝘳𝘳.𝘮𝘦𝘴𝘴𝘢𝘨𝘦,&lt;br&gt;
      𝘴𝘵𝘢𝘵𝘶𝘴𝘊𝘰𝘥𝘦: 𝘦𝘳𝘳.𝘴𝘵𝘢𝘵𝘶𝘴𝘊𝘰𝘥𝘦,&lt;br&gt;
    });&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;𝘳𝘦𝘴.𝘴𝘵𝘢𝘵𝘶𝘴(𝘦𝘳𝘳.𝘴𝘵𝘢𝘵𝘶𝘴𝘊𝘰𝘥𝘦).𝘫𝘴𝘰𝘯({
  𝘴𝘵𝘢𝘵𝘶𝘴: '𝘦𝘳𝘳𝘰𝘳',
  𝘤𝘰𝘥𝘦: 𝘦𝘳𝘳.𝘤𝘰𝘥𝘦,
  𝘮𝘦𝘴𝘴𝘢𝘨𝘦: 𝘦𝘳𝘳.𝘮𝘦𝘴𝘴𝘢𝘨𝘦,
});
𝘳𝘦𝘵𝘶𝘳𝘯;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;// 𝘜𝘯𝘦𝘹𝘱𝘦𝘤𝘵𝘦𝘥 𝘦𝘳𝘳𝘰𝘳 — 𝘭𝘰𝘨 𝘦𝘷𝘦𝘳𝘺𝘵𝘩𝘪𝘯𝘨, 𝘳𝘦𝘴𝘱𝘰𝘯𝘥 𝘨𝘦𝘯𝘦𝘳𝘪𝘤𝘢𝘭𝘭𝘺&lt;br&gt;
  𝘭𝘰𝘨𝘨𝘦𝘳.𝘦𝘳𝘳𝘰𝘳({&lt;br&gt;
    𝘳𝘦𝘲𝘶𝘦𝘴𝘵𝘐𝘥,&lt;br&gt;
    𝘮𝘦𝘴𝘴𝘢𝘨𝘦: 𝘦𝘳𝘳.𝘮𝘦𝘴𝘴𝘢𝘨𝘦,&lt;br&gt;
    𝘴𝘵𝘢𝘤𝘬: 𝘦𝘳𝘳.𝘴𝘵𝘢𝘤𝘬,&lt;br&gt;
    𝘯𝘢𝘮𝘦: 𝘦𝘳𝘳.𝘯𝘢𝘮𝘦,&lt;br&gt;
  });&lt;/p&gt;

&lt;p&gt;𝘳𝘦𝘴.𝘴𝘵𝘢𝘵𝘶𝘴(500).𝘫𝘴𝘰𝘯({&lt;br&gt;
    𝘴𝘵𝘢𝘵𝘶𝘴: '𝘦𝘳𝘳𝘰𝘳',&lt;br&gt;
    𝘤𝘰𝘥𝘦: '𝘐𝘕𝘛𝘌𝘙𝘕𝘈𝘓_𝘌𝘙𝘙𝘖𝘙',&lt;br&gt;
    𝘮𝘦𝘴𝘴𝘢𝘨𝘦: '𝘚𝘰𝘮𝘦𝘵𝘩𝘪𝘯𝘨 𝘸𝘦𝘯𝘵 𝘸𝘳𝘰𝘯𝘨. 𝘗𝘭𝘦𝘢𝘴𝘦 𝘵𝘳𝘺 𝘢𝘨𝘢𝘪𝘯.',&lt;br&gt;
  });&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Clients never see stack traces, internal messages, or database query strings. Logs capture everything you need to debug. The requestId ties the log line back to the exact request that failed.&lt;/p&gt;




&lt;p&gt;𝗣𝗮𝗿𝘁 𝟯: 𝗔𝗻 𝗮𝘀𝘆𝗻𝗰 𝘄𝗿𝗮𝗽𝗽𝗲𝗿 𝘁𝗼 𝗰𝗮𝘁𝗰𝗵 𝗿𝗼𝘂𝘁𝗲 𝗲𝗿𝗿𝗼𝗿𝘀&lt;br&gt;
Express doesn't catch errors thrown inside async route handlers unless you explicitly call next(err). Writing try/catch in every route is noisy and forgettable.&lt;br&gt;
This wrapper handles it once:&lt;/p&gt;

&lt;p&gt;// 𝘴𝘳𝘤/𝘶𝘵𝘪𝘭𝘴/𝘢𝘴𝘺𝘯𝘤𝘏𝘢𝘯𝘥𝘭𝘦𝘳.𝘵𝘴&lt;br&gt;
𝘪𝘮𝘱𝘰𝘳𝘵 { 𝘙𝘦𝘲𝘶𝘦𝘴𝘵, 𝘙𝘦𝘴𝘱𝘰𝘯𝘴𝘦, 𝘕𝘦𝘹𝘵𝘍𝘶𝘯𝘤𝘵𝘪𝘰𝘯, 𝘙𝘦𝘲𝘶𝘦𝘴𝘵𝘏𝘢𝘯𝘥𝘭𝘦𝘳 } 𝘧𝘳𝘰𝘮 '𝘦𝘹𝘱𝘳𝘦𝘴𝘴';&lt;/p&gt;

&lt;p&gt;𝘦𝘹𝘱𝘰𝘳𝘵 𝘧𝘶𝘯𝘤𝘵𝘪𝘰𝘯 𝘢𝘴𝘺𝘯𝘤𝘏𝘢𝘯𝘥𝘭𝘦𝘳(𝘧𝘯: 𝘙𝘦𝘲𝘶𝘦𝘴𝘵𝘏𝘢𝘯𝘥𝘭𝘦𝘳): 𝘙𝘦𝘲𝘶𝘦𝘴𝘵𝘏𝘢𝘯𝘥𝘭𝘦𝘳 {&lt;br&gt;
  𝘳𝘦𝘵𝘶𝘳𝘯 (𝘳𝘦𝘲: 𝘙𝘦𝘲𝘶𝘦𝘴𝘵, 𝘳𝘦𝘴: 𝘙𝘦𝘴𝘱𝘰𝘯𝘴𝘦, 𝘯𝘦𝘹𝘵: 𝘕𝘦𝘹𝘵𝘍𝘶𝘯𝘤𝘵𝘪𝘰𝘯) =&amp;gt; {&lt;br&gt;
    𝘗𝘳𝘰𝘮𝘪𝘴𝘦.𝘳𝘦𝘴𝘰𝘭𝘷𝘦(𝘧𝘯(𝘳𝘦𝘲, 𝘳𝘦𝘴, 𝘯𝘦𝘹𝘵)).𝘤𝘢𝘵𝘤𝘩(𝘯𝘦𝘹𝘵);&lt;br&gt;
  };&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;Now routes look like this:&lt;/p&gt;

&lt;p&gt;// 𝘴𝘳𝘤/𝘳𝘰𝘶𝘵𝘦𝘴/𝘶𝘴𝘦𝘳𝘴.𝘵𝘴&lt;br&gt;
𝘪𝘮𝘱𝘰𝘳𝘵 { 𝘙𝘰𝘶𝘵𝘦𝘳 } 𝘧𝘳𝘰𝘮 '𝘦𝘹𝘱𝘳𝘦𝘴𝘴';&lt;br&gt;
𝘪𝘮𝘱𝘰𝘳𝘵 { 𝘢𝘴𝘺𝘯𝘤𝘏𝘢𝘯𝘥𝘭𝘦𝘳 } 𝘧𝘳𝘰𝘮 '../𝘶𝘵𝘪𝘭𝘴/𝘢𝘴𝘺𝘯𝘤𝘏𝘢𝘯𝘥𝘭𝘦𝘳';&lt;br&gt;
𝘪𝘮𝘱𝘰𝘳𝘵 { 𝘕𝘰𝘵𝘍𝘰𝘶𝘯𝘥𝘌𝘳𝘳𝘰𝘳 } 𝘧𝘳𝘰𝘮 '../𝘦𝘳𝘳𝘰𝘳𝘴/𝘈𝘱𝘱𝘌𝘳𝘳𝘰𝘳';&lt;br&gt;
𝘪𝘮𝘱𝘰𝘳𝘵 { 𝘶𝘴𝘦𝘳𝘚𝘦𝘳𝘷𝘪𝘤𝘦 } 𝘧𝘳𝘰𝘮 '../𝘴𝘦𝘳𝘷𝘪𝘤𝘦𝘴/𝘶𝘴𝘦𝘳𝘚𝘦𝘳𝘷𝘪𝘤𝘦';&lt;/p&gt;

&lt;p&gt;𝘤𝘰𝘯𝘴𝘵 𝘳𝘰𝘶𝘵𝘦𝘳 = 𝘙𝘰𝘶𝘵𝘦𝘳();&lt;/p&gt;

&lt;p&gt;𝘳𝘰𝘶𝘵𝘦𝘳.𝘨𝘦𝘵('/:𝘪𝘥', 𝘢𝘴𝘺𝘯𝘤𝘏𝘢𝘯𝘥𝘭𝘦𝘳(𝘢𝘴𝘺𝘯𝘤 (𝘳𝘦𝘲, 𝘳𝘦𝘴) =&amp;gt; {&lt;br&gt;
  𝘤𝘰𝘯𝘴𝘵 𝘶𝘴𝘦𝘳 = 𝘢𝘸𝘢𝘪𝘵 𝘶𝘴𝘦𝘳𝘚𝘦𝘳𝘷𝘪𝘤𝘦.𝘧𝘪𝘯𝘥𝘉𝘺𝘐𝘥(𝘳𝘦𝘲.𝘱𝘢𝘳𝘢𝘮𝘴.𝘪𝘥);&lt;/p&gt;

&lt;p&gt;𝘪𝘧 (!𝘶𝘴𝘦𝘳) {&lt;br&gt;
    𝘵𝘩𝘳𝘰𝘸 𝘯𝘦𝘸 𝘕𝘰𝘵𝘍𝘰𝘶𝘯𝘥𝘌𝘳𝘳𝘰𝘳('𝘜𝘴𝘦𝘳'); // 𝘧𝘭𝘰𝘸𝘴 𝘴𝘵𝘳𝘢𝘪𝘨𝘩𝘵 𝘵𝘰 𝘦𝘳𝘳𝘰𝘳𝘏𝘢𝘯𝘥𝘭𝘦𝘳&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;𝘳𝘦𝘴.𝘫𝘴𝘰𝘯({ 𝘴𝘵𝘢𝘵𝘶𝘴: '𝘴𝘶𝘤𝘤𝘦𝘴𝘴', 𝘥𝘢𝘵𝘢: 𝘶𝘴𝘦𝘳 });&lt;br&gt;
}));&lt;/p&gt;

&lt;p&gt;𝘦𝘹𝘱𝘰𝘳𝘵 𝘥𝘦𝘧𝘢𝘶𝘭𝘵 𝘳𝘰𝘶𝘵𝘦𝘳;&lt;/p&gt;

&lt;p&gt;No try/catch in the route. No next(err) calls manually. Any thrown error — expected or not — flows directly to your central error handler.&lt;/p&gt;




&lt;p&gt;𝗣𝗮𝗿𝘁 𝟰: 𝗪𝗶𝗿𝗶𝗻𝗴 𝗶𝘁 𝗮𝗹𝗹 𝘁𝗼𝗴𝗲𝘁𝗵𝗲𝗿 𝗶𝗻 𝗘𝘅𝗽𝗿𝗲𝘀𝘀&lt;/p&gt;

&lt;p&gt;// 𝘴𝘳𝘤/𝘢𝘱𝘱.𝘵𝘴&lt;br&gt;
𝘪𝘮𝘱𝘰𝘳𝘵 𝘦𝘹𝘱𝘳𝘦𝘴𝘴 𝘧𝘳𝘰𝘮 '𝘦𝘹𝘱𝘳𝘦𝘴𝘴';&lt;br&gt;
𝘪𝘮𝘱𝘰𝘳𝘵 { 𝘳𝘢𝘯𝘥𝘰𝘮𝘜𝘜𝘐𝘋 } 𝘧𝘳𝘰𝘮 '𝘤𝘳𝘺𝘱𝘵𝘰';&lt;br&gt;
𝘪𝘮𝘱𝘰𝘳𝘵 𝘶𝘴𝘦𝘳𝘙𝘰𝘶𝘵𝘦𝘴 𝘧𝘳𝘰𝘮 './𝘳𝘰𝘶𝘵𝘦𝘴/𝘶𝘴𝘦𝘳𝘴';&lt;br&gt;
𝘪𝘮𝘱𝘰𝘳𝘵 { 𝘦𝘳𝘳𝘰𝘳𝘏𝘢𝘯𝘥𝘭𝘦𝘳 } 𝘧𝘳𝘰𝘮 './𝘮𝘪𝘥𝘥𝘭𝘦𝘸𝘢𝘳𝘦/𝘦𝘳𝘳𝘰𝘳𝘏𝘢𝘯𝘥𝘭𝘦𝘳';&lt;/p&gt;

&lt;p&gt;𝘤𝘰𝘯𝘴𝘵 𝘢𝘱𝘱 = 𝘦𝘹𝘱𝘳𝘦𝘴𝘴();&lt;/p&gt;

&lt;p&gt;𝘢𝘱𝘱.𝘶𝘴𝘦(𝘦𝘹𝘱𝘳𝘦𝘴𝘴.𝘫𝘴𝘰𝘯());&lt;/p&gt;

&lt;p&gt;// 𝘈𝘵𝘵𝘢𝘤𝘩 𝘳𝘦𝘲𝘶𝘦𝘴𝘵 𝘐𝘋 𝘵𝘰 𝘦𝘷𝘦𝘳𝘺 𝘳𝘦𝘲𝘶𝘦𝘴𝘵&lt;br&gt;
𝘢𝘱𝘱.𝘶𝘴𝘦((𝘳𝘦𝘲, 𝘳𝘦𝘴, 𝘯𝘦𝘹𝘵) =&amp;gt; {&lt;br&gt;
  𝘤𝘰𝘯𝘴𝘵 𝘳𝘦𝘲𝘶𝘦𝘴𝘵𝘐𝘥 = (𝘳𝘦𝘲.𝘩𝘦𝘢𝘥𝘦𝘳𝘴['𝘹-𝘳𝘦𝘲𝘶𝘦𝘴𝘵-𝘪𝘥'] 𝘢𝘴 𝘴𝘵𝘳𝘪𝘯𝘨) || 𝘳𝘢𝘯𝘥𝘰𝘮𝘜𝘜𝘐𝘋();&lt;br&gt;
  𝘳𝘦𝘲.𝘩𝘦𝘢𝘥𝘦𝘳𝘴['𝘹-𝘳𝘦𝘲𝘶𝘦𝘴𝘵-𝘪𝘥'] = 𝘳𝘦𝘲𝘶𝘦𝘴𝘵𝘐𝘥;&lt;br&gt;
  𝘳𝘦𝘴.𝘴𝘦𝘵𝘏𝘦𝘢𝘥𝘦𝘳('𝘹-𝘳𝘦𝘲𝘶𝘦𝘴𝘵-𝘪𝘥', 𝘳𝘦𝘲𝘶𝘦𝘴𝘵𝘐𝘥);&lt;br&gt;
  𝘯𝘦𝘹𝘵();&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;// 𝘙𝘰𝘶𝘵𝘦𝘴&lt;br&gt;
𝘢𝘱𝘱.𝘶𝘴𝘦('/𝘢𝘱𝘪/𝘶𝘴𝘦𝘳𝘴', 𝘶𝘴𝘦𝘳𝘙𝘰𝘶𝘵𝘦𝘴);&lt;/p&gt;

&lt;p&gt;// 404 𝘩𝘢𝘯𝘥𝘭𝘦𝘳 — 𝘮𝘶𝘴𝘵 𝘤𝘰𝘮𝘦 𝘢𝘧𝘵𝘦𝘳 𝘢𝘭𝘭 𝘳𝘰𝘶𝘵𝘦𝘴&lt;br&gt;
𝘢𝘱𝘱.𝘶𝘴𝘦((𝘳𝘦𝘲, 𝘳𝘦𝘴, 𝘯𝘦𝘹𝘵) =&amp;gt; {&lt;br&gt;
  𝘯𝘦𝘹𝘵(𝘯𝘦𝘸 𝘕𝘰𝘵𝘍𝘰𝘶𝘯𝘥𝘌𝘳𝘳𝘰𝘳(&lt;code&gt;𝘙𝘰𝘶𝘵𝘦 ${𝘳𝘦𝘲.𝘮𝘦𝘵𝘩𝘰𝘥} ${𝘳𝘦𝘲.𝘱𝘢𝘵𝘩}&lt;/code&gt;));&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;// 𝘊𝘦𝘯𝘵𝘳𝘢𝘭 𝘦𝘳𝘳𝘰𝘳 𝘩𝘢𝘯𝘥𝘭𝘦𝘳 — 𝘮𝘶𝘴𝘵 𝘣𝘦 𝘭𝘢𝘴𝘵, 𝘮𝘶𝘴𝘵 𝘩𝘢𝘷𝘦 4 𝘱𝘢𝘳𝘢𝘮𝘴&lt;br&gt;
𝘢𝘱𝘱.𝘶𝘴𝘦(𝘦𝘳𝘳𝘰𝘳𝘏𝘢𝘯𝘥𝘭𝘦𝘳);&lt;/p&gt;

&lt;p&gt;𝘦𝘹𝘱𝘰𝘳𝘵 𝘥𝘦𝘧𝘢𝘶𝘭𝘵 𝘢𝘱𝘱;&lt;/p&gt;

&lt;p&gt;The order matters. The errorHandler must be registered last. The 404 handler must come after all your routes. The request ID middleware must come first so every log line has it.&lt;/p&gt;




&lt;p&gt;𝗪𝗵𝗮𝘁 𝘁𝗵𝗶𝘀 𝗴𝗶𝘃𝗲𝘀 𝘆𝗼𝘂 𝗶𝗻 𝗽𝗿𝗼𝗱𝘂𝗰𝘁𝗶𝗼𝗻&lt;/p&gt;

&lt;p&gt;Every error in your API now has:&lt;br&gt;
• A typed code field clients can handle programmatically (NOT_FOUND, UNAUTHORIZED, VALIDATION_ERROR)&lt;/p&gt;

&lt;p&gt;• A clean message safe to show users&lt;/p&gt;

&lt;p&gt;• A requestId that ties logs across every layer&lt;/p&gt;

&lt;p&gt;• Full stack traces in logs for unexpected failures&lt;/p&gt;

&lt;p&gt;• Zero internal detail leaked to the outside world&lt;/p&gt;

&lt;p&gt;When something breaks at 2am, you grep the requestId from the client's error response and pull the full picture from your logs in seconds — not hours.&lt;/p&gt;

&lt;p&gt;𝑠𝑟𝑐/&lt;br&gt;
├── 𝑒𝑟𝑟𝑜𝑟𝑠/&lt;br&gt;
│   └── 𝐴𝑝𝑝𝐸𝑟𝑟𝑜𝑟.𝑡𝑠          # 𝑡𝑦𝑝𝑒𝑑 𝑒𝑟𝑟𝑜𝑟 𝑐𝑙𝑎𝑠𝑠𝑒𝑠&lt;br&gt;
├── 𝑚𝑖𝑑𝑑𝑙𝑒𝑤𝑎𝑟𝑒/&lt;br&gt;
│   └── 𝑒𝑟𝑟𝑜𝑟𝐻𝑎𝑛𝑑𝑙𝑒𝑟.𝑡𝑠      # 𝑐𝑒𝑛𝑡𝑟𝑎𝑙 𝑒𝑟𝑟𝑜𝑟 ℎ𝑎𝑛𝑑𝑙𝑒𝑟&lt;br&gt;
├── 𝑢𝑡𝑖𝑙𝑠/&lt;br&gt;
│   └── 𝑎𝑠𝑦𝑛𝑐𝐻𝑎𝑛𝑑𝑙𝑒𝑟.𝑡𝑠      # 𝑎𝑠𝑦𝑛𝑐 𝑟𝑜𝑢𝑡𝑒 𝑤𝑟𝑎𝑝𝑝𝑒𝑟&lt;br&gt;
├── 𝑟𝑜𝑢𝑡𝑒𝑠/&lt;br&gt;
│   └── 𝑢𝑠𝑒𝑟𝑠.𝑡𝑠             # 𝑐𝑙𝑒𝑎𝑛 𝑟𝑜𝑢𝑡𝑒𝑠, 𝑛𝑜 𝑡𝑟𝑦/𝑐𝑎𝑡𝑐ℎ&lt;br&gt;
└── 𝑎𝑝𝑝.𝑡𝑠                   # 𝑤𝑖𝑟𝑒𝑑 𝑡𝑜𝑔𝑒𝑡ℎ𝑒𝑟&lt;/p&gt;




&lt;p&gt;𝗢𝗻𝗲 𝘁𝗵𝗶𝗻𝗴 𝘁𝗼 𝗮𝗱𝗱 𝗻𝗲𝘅𝘁&lt;/p&gt;

&lt;p&gt;If you want to go further, add a process-level handler for truly unexpected crashes:&lt;/p&gt;

&lt;p&gt;// 𝘴𝘳𝘤/𝘴𝘦𝘳𝘷𝘦𝘳.𝘵𝘴&lt;br&gt;
𝘱𝘳𝘰𝘤𝘦𝘴𝘴.𝘰𝘯('𝘶𝘯𝘤𝘢𝘶𝘨𝘩𝘵𝘌𝘹𝘤𝘦𝘱𝘵𝘪𝘰𝘯', (𝘦𝘳𝘳) =&amp;gt; {&lt;br&gt;
  𝘭𝘰𝘨𝘨𝘦𝘳.𝘧𝘢𝘵𝘢𝘭({ 𝘦𝘳𝘳 }, '𝘜𝘯𝘤𝘢𝘶𝘨𝘩𝘵 𝘦𝘹𝘤𝘦𝘱𝘵𝘪𝘰𝘯 — 𝘴𝘩𝘶𝘵𝘵𝘪𝘯𝘨 𝘥𝘰𝘸𝘯');&lt;br&gt;
  𝘱𝘳𝘰𝘤𝘦𝘴𝘴.𝘦𝘹𝘪𝘵(1);&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;𝘱𝘳𝘰𝘤𝘦𝘴𝘴.𝘰𝘯('𝘶𝘯𝘩𝘢𝘯𝘥𝘭𝘦𝘥𝘙𝘦𝘫𝘦𝘤𝘵𝘪𝘰𝘯', (𝘳𝘦𝘢𝘴𝘰𝘯) =&amp;gt; {&lt;br&gt;
  𝘭𝘰𝘨𝘨𝘦𝘳.𝘧𝘢𝘵𝘢𝘭({ 𝘳𝘦𝘢𝘴𝘰𝘯 }, '𝘜𝘯𝘩𝘢𝘯𝘥𝘭𝘦𝘥 𝘳𝘦𝘫𝘦𝘤𝘵𝘪𝘰𝘯 — 𝘴𝘩𝘶𝘵𝘵𝘪𝘯𝘨 𝘥𝘰𝘸𝘯');&lt;br&gt;
  𝘱𝘳𝘰𝘤𝘦𝘴𝘴.𝘦𝘹𝘪𝘵(1);&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkujq0atn3talsmj0mhhf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkujq0atn3talsmj0mhhf.png" alt=" " width="800" height="602"&gt;&lt;/a&gt;&lt;br&gt;
Fail fast. Fail loud. Let your process manager (PM2, Docker, Kubernetes) restart the service. Never let your app limp along in a broken state.&lt;br&gt;
This pattern takes about 20 minutes to wire up on a new project. It has saved me hours of debugging on every project since.&lt;/p&gt;




&lt;p&gt;If you're building a REST API in Express + TypeScript and you don't have something like this in place — start here before you add another feature.&lt;/p&gt;




&lt;p&gt;𝐴𝑟𝑒 𝑦𝑜𝑢 𝑢𝑠𝑖𝑛𝑔 𝑎 𝑑𝑖𝑓𝑓𝑒𝑟𝑒𝑛𝑡 𝑒𝑟𝑟𝑜𝑟 ℎ𝑎𝑛𝑑𝑙𝑖𝑛𝑔 𝑝𝑎𝑡𝑡𝑒𝑟𝑛 𝑖𝑛 𝑝𝑟𝑜𝑑𝑢𝑐𝑡𝑖𝑜𝑛? 𝑆𝑜𝑚𝑒𝑡ℎ𝑖𝑛𝑔 𝑦𝑜𝑢'𝑑 𝑎𝑑𝑑 𝑜𝑟 𝑐ℎ𝑎𝑛𝑔𝑒 ℎ𝑒𝑟𝑒? 𝐷𝑟𝑜𝑝 𝑖𝑡 𝑖𝑛 𝑡ℎ𝑒 𝑐𝑜𝑚𝑚𝑒𝑛𝑡𝑠 — 𝐼'𝑚 𝑔𝑒𝑛𝑢𝑖𝑛𝑒𝑙𝑦 𝑐𝑢𝑟𝑖𝑜𝑢𝑠 𝑤ℎ𝑎𝑡 𝑜𝑡ℎ𝑒𝑟𝑠 ℎ𝑎𝑣𝑒 𝑙𝑎𝑛𝑑𝑒𝑑 𝑜𝑛.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>typescript</category>
      <category>node</category>
    </item>
    <item>
      <title>How I Fixed a Race Condition in a Live Seat Booking System (And Lost Sleep Over It)</title>
      <dc:creator>Jeremiah Deku</dc:creator>
      <pubDate>Sun, 29 Mar 2026 00:28:15 +0000</pubDate>
      <link>https://dev.to/jeremiah_dek/how-i-fixed-a-race-condition-in-a-live-seat-booking-system-and-lost-sleep-over-it-48ii</link>
      <guid>https://dev.to/jeremiah_dek/how-i-fixed-a-race-condition-in-a-live-seat-booking-system-and-lost-sleep-over-it-48ii</guid>
      <description>&lt;p&gt;𝗧𝘄𝗼 𝘂𝘀𝗲𝗿𝘀. 𝗢𝗻𝗲 𝘀𝗲𝗮𝘁. 𝗧𝗼𝘁𝗮𝗹 𝗰𝗵𝗮𝗼𝘀.&lt;br&gt;
It was a regular Tuesday on a client project — a bus ticket booking platform. Everything looked fine until a support ticket landed: two passengers had been assigned the same seat on the same trip.&lt;/p&gt;

&lt;p&gt;My first instinct? "That can't happen — I have a check before booking." Famous last words.&lt;/p&gt;

&lt;h2&gt;
  
  
  𝗪𝗵𝗮𝘁 𝘄𝗮𝘀 𝗮𝗰𝘁𝘂𝗮𝗹𝗹𝘆 𝗵𝗮𝗽𝗽𝗲𝗻𝗶𝗻𝗴:
&lt;/h2&gt;

&lt;p&gt;User A and User B both queried the seat at nearly the same millisecond. Both saw it as available. Both proceeded to book. The database updated twice — same seat, two owners.&lt;/p&gt;

&lt;h2&gt;
  
  
  𝗠𝘆 𝗳𝗶𝗿𝘀𝘁 (𝘄𝗿𝗼𝗻𝗴) 𝗮𝘁𝘁𝗲𝗺𝗽𝘁𝘀
&lt;/h2&gt;

&lt;p&gt;I tried a simple if check before updating — reading the seat status, then writing. Seemed logical. Broke immediately under any real concurrency. Read-then-write is not atomic. The window between those two operations is exactly where race conditions live.&lt;/p&gt;

&lt;p&gt;Then I tried a client-side flag. Also wrong. Socket.io events can arrive out of order, and the UI state is not a source of truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: atomic update + real-time lock broadcast
&lt;/h2&gt;

&lt;p&gt;The solution was to collapse the read and the write into a single atomic MongoDB operation — and pair it with a Socket.io broadcast so every connected client sees the lock instantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  // Atomic Updates + Socket.io Real-Time Lock
&lt;/h2&gt;

&lt;p&gt;async function reserveSeat(tripId, seatNo, userId) {&lt;/p&gt;

&lt;p&gt;// Prevents Race Conditions&lt;br&gt;
  const trip = await Trip.findOneAndUpdate(&lt;br&gt;
    { _id: tripId, "seats.no": seatNo, "seats.status": "available" },&lt;br&gt;
    { $set: { "seats.$.status": "locked", "seats.$.heldBy": userId } },&lt;br&gt;
    { new: true }&lt;br&gt;
  );&lt;/p&gt;

&lt;p&gt;if (!trip) throw new Error("Seat already taken!");&lt;/p&gt;

&lt;p&gt;// Broadcast the 'Lock' to all travelers&lt;br&gt;
  io.emit("seat_locked", { tripId, seatNo });&lt;br&gt;
  console.log(&lt;code&gt;Seat ${seatNo} secured for User ${userId}&lt;/code&gt;);&lt;br&gt;
}&lt;/p&gt;

&lt;h2&gt;
  
  
  𝗪𝗵𝘆 𝘁𝗵𝗶𝘀 𝘄𝗼𝗿𝗸𝘀 — 𝗹𝗶𝗻𝗲 𝗯𝘆 𝗹𝗶𝗻𝗲
&lt;/h2&gt;

&lt;p&gt;1️⃣ 𝙏𝙝𝙚 𝙦𝙪𝙚𝙧𝙮 𝙘𝙤𝙣𝙙𝙞𝙩𝙞𝙤𝙣 𝙞𝙨 𝙩𝙝𝙚 𝙜𝙪𝙖𝙧𝙙&lt;br&gt;
"seats.status": "available" means MongoDB only updates if the seat is still available at the exact moment of the write. If two requests arrive simultaneously, only one will match — the other gets null.&lt;/p&gt;

&lt;p&gt;2️⃣ 𝙛𝙞𝙣𝙙𝙊𝙣𝙚𝘼𝙣𝙙𝙐𝙥𝙙𝙖𝙩𝙚 𝙞𝙨 𝙖𝙩𝙤𝙢𝙞𝙘 𝙖𝙩 𝙩𝙝𝙚 𝙙𝙤𝙘𝙪𝙢𝙚𝙣𝙩 𝙡𝙚𝙫𝙚𝙡&lt;br&gt;
MongoDB guarantees that the find-and-update happens as a single operation. No other write can slip in between. This is the core of the fix.&lt;/p&gt;

&lt;p&gt;3️⃣ 𝙏𝙝𝙚 𝙣𝙪𝙡𝙡 𝙘𝙝𝙚𝙘𝙠 𝙞𝙨 𝙮𝙤𝙪𝙧 𝙚𝙧𝙧𝙤𝙧 𝙗𝙤𝙪𝙣𝙙𝙖𝙧𝙮&lt;br&gt;
If trip is null, the seat was already taken. Throw a clear error — don't silently fail. The client can catch this and show the user a friendly message.&lt;/p&gt;

&lt;p&gt;4️⃣ 𝙎𝙤𝙘𝙠𝙚𝙩.𝙞𝙤 𝙗𝙧𝙤𝙖𝙙𝙘𝙖𝙨𝙩𝙨 𝙩𝙝𝙚 𝙡𝙤𝙘𝙠 𝙞𝙢𝙢𝙚𝙙𝙞𝙖𝙩𝙚𝙡𝙮&lt;br&gt;
Once the DB confirms the lock, we emit seat_locked to all connected clients. Every user's seat map updates in real time — no polling, no stale UI.&lt;/p&gt;

&lt;p&gt;𝗧𝗵𝗲 𝗯𝗶𝗴𝗴𝗲𝘀𝘁 𝗹𝗲𝘀𝘀𝗼𝗻&lt;br&gt;
Race conditions don't announce themselves. They hide behind low traffic and happy paths. This bug only surfaced because two users happened to book at the same moment — something that's guaranteed to happen at scale.&lt;/p&gt;

&lt;p&gt;The fix isn't just about MongoDB. It's a mindset shift: never trust a read before a write in a concurrent system. Push your guard logic into the database operation itself, where it's atomic.&lt;/p&gt;

&lt;p&gt;𝐻𝑎𝑣𝑒 𝑦𝑜𝑢 𝑟𝑢𝑛 𝑖𝑛𝑡𝑜 𝑟𝑎𝑐𝑒 𝑐𝑜𝑛𝑑𝑖𝑡𝑖𝑜𝑛𝑠 𝑖𝑛 𝑦𝑜𝑢𝑟 𝑜𝑤𝑛 𝑝𝑟𝑜𝑗𝑒𝑐𝑡𝑠? 𝐼'𝑑 𝑙𝑜𝑣𝑒 𝑡𝑜 ℎ𝑒𝑎𝑟 ℎ𝑜𝑤 𝑦𝑜𝑢 ℎ𝑎𝑛𝑑𝑙𝑒𝑑 𝑖𝑡.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzm5u0irvpynerlxeqq2e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzm5u0irvpynerlxeqq2e.png" alt=" " width="800" height="948"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>notionchallenge</category>
      <category>ai</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
