<?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: Mzer Terdoo</title>
    <description>The latest articles on DEV Community by Mzer Terdoo (@terdoo-mzer).</description>
    <link>https://dev.to/terdoo-mzer</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F637982%2Fc46d6275-a50c-477f-9a85-5a0b67fc5e31.jpeg</url>
      <title>DEV Community: Mzer Terdoo</title>
      <link>https://dev.to/terdoo-mzer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/terdoo-mzer"/>
    <language>en</language>
    <item>
      <title>A Node.js Bug That Taught Me About the Current Working Directory (CWD)</title>
      <dc:creator>Mzer Terdoo</dc:creator>
      <pubDate>Thu, 06 Aug 2026 21:49:48 +0000</pubDate>
      <link>https://dev.to/terdoo-mzer/a-nodejs-bug-that-taught-me-about-the-current-working-directory-cwd-4k6f</link>
      <guid>https://dev.to/terdoo-mzer/a-nodejs-bug-that-taught-me-about-the-current-working-directory-cwd-4k6f</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg0a9hkn9ds5vc2dldbe4.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fg0a9hkn9ds5vc2dldbe4.png" alt="A Node.js Bug That Taught Me About the Current Working Directory (CWD)" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are bugs that make you question your code, and then there are bugs that make you question your understanding of how a language works.&lt;/p&gt;

&lt;p&gt;This was the latter.&lt;/p&gt;

&lt;p&gt;Recently, while wiriting a script in a project I have been on for quite a while now, I encountered an issue that opened my eyes to understanding the concept of &lt;strong&gt;Current Working Directory&lt;/strong&gt;. Interestingly, I only got to know about this concept because of the snag I experienced.&lt;/p&gt;

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

&lt;p&gt;To put things in context, the “aha” moment came while working on the &lt;strong&gt;Single Flight Pattern (SFP)&lt;/strong&gt; implementation which I talked about in this writeup — you can read it up here (insert link here).&lt;br&gt;&lt;br&gt;
I needed to test the SFP implementation in isolation from the rest of the project, so I wrote the test script, and attempted running it from the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node test-SFP.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of seeing my test pass, I got the error in the command line shown in the screenshot below (emphasis on the lines with a yellow pointer/arrow);&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz4ifb7ulgfl05mndo1j0.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz4ifb7ulgfl05mndo1j0.png" alt="COmmand Line" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first, I assumed I had broken something in the main SFP code I had written.&lt;/p&gt;

&lt;p&gt;I hadn’t.&lt;/p&gt;

&lt;h3&gt;
  
  
  My First Assumption
&lt;/h3&gt;

&lt;p&gt;Since my OAuth client relied on environment variables, I naturally suspected that the variable was not spelt correctly, so I deliberately deleted and rewrote that and retried running the script again, but got same result. I even went ahead to log other . &lt;strong&gt;env&lt;/strong&gt; variables to be sure I could isolate the issue, but each time, I got an &lt;strong&gt;undefined&lt;/strong&gt; printed in the console.&lt;/p&gt;

&lt;p&gt;I also at some point begin to change the order of imports in my code to be sure I was not improperly importing related files.&lt;/p&gt;

&lt;p&gt;At that point, I even started wondering whether I had misunderstood how ES Modules loaded dependencies.&lt;/p&gt;

&lt;p&gt;Looking back, I was making a classic debugging mistake.&lt;/p&gt;

&lt;p&gt;I was changing code before I had enough information about the problem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Time to Stop Guessing
&lt;/h3&gt;

&lt;p&gt;Instead of continuing to make changes, I decided to gather evidence.&lt;/p&gt;

&lt;p&gt;The first thing I did was to log &lt;strong&gt;dotenv.config()&lt;/strong&gt; (&lt;em&gt;note that I imported this module into the test file because I was testing the SFP part of my application in isolation&lt;/em&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dotenv&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;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output immediately changed the direction of my investigation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ENOENT: no such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See full output of the error / output in the screenshot attached below:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F04k7l6zcsx863si66b1e.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F04k7l6zcsx863si66b1e.png" alt="Command Line" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The output in the screenshot was an important discovery for me!&lt;br&gt;&lt;br&gt;
It said there was no  &lt;strong&gt;.env&lt;/strong&gt; , yet I was sure it existed — in the project root!&lt;/p&gt;

&lt;p&gt;So why was  &lt;strong&gt;.dotenv&lt;/strong&gt; insisting that it didn't?&lt;/p&gt;
&lt;h3&gt;
  
  
  The Clue Was Hidden in the Error Message
&lt;/h3&gt;

&lt;p&gt;Reading the error more carefully, I noticed something I had completely overlooked.&lt;/p&gt;

&lt;p&gt;The error wasn’t simply saying that  &lt;strong&gt;.env&lt;/strong&gt; couldn't be found.&lt;/p&gt;

&lt;p&gt;It was telling me &lt;strong&gt;where it was looking&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C:\..\..\bot\src\services\.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My actual  &lt;strong&gt;.env&lt;/strong&gt; file wasn't there.&lt;/p&gt;

&lt;p&gt;It lived here instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;C:\..\..\bot\.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That completely changed the problem.&lt;/p&gt;

&lt;p&gt;The issue was no longer:&lt;br&gt;&lt;br&gt;
“ &lt;strong&gt;dotenv&lt;/strong&gt; can't find my &lt;strong&gt;.env&lt;/strong&gt; file."&lt;/p&gt;

&lt;p&gt;The issue became:&lt;br&gt;&lt;br&gt;
“Why is it looking inside &lt;strong&gt;src/services&lt;/strong&gt; in the first place?"&lt;/p&gt;

&lt;p&gt;That question eventually led me to the real lesson.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Discovery
&lt;/h3&gt;

&lt;p&gt;This debugging session introduced me to a Node.js concept that, despite using Node for quite some time, I had never really stopped to think about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Current Working Directory (CWD).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Up until this point, I had assumed that because I was executing &lt;strong&gt;test-SFP.js,&lt;/strong&gt; any library — in this case &lt;strong&gt;dotenv&lt;/strong&gt; that needed to find a file would automatically look relative to &lt;strong&gt;test-SFP.js&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That wasn’t the case.&lt;/p&gt;

&lt;p&gt;When I called:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dotenv&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;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wasn’t telling &lt;strong&gt;dotenv&lt;/strong&gt; where my  &lt;strong&gt;.env&lt;/strong&gt; file lived.&lt;/p&gt;

&lt;p&gt;So &lt;strong&gt;dotenv&lt;/strong&gt; had to make a choice.&lt;/p&gt;

&lt;p&gt;By default, it asked Node for the application’s &lt;strong&gt;Current Working Directory&lt;/strong&gt; using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and then looked for a  &lt;strong&gt;.env&lt;/strong&gt; file there.&lt;/p&gt;

&lt;p&gt;This made me curious, so I printed the Current Working Directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I executed my test like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;src/services
node test-SFP.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the output was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.../bot/src/services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That meant &lt;strong&gt;dotenv&lt;/strong&gt; naturally searched for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bot/
└── src/
    └── services/
        └── .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But my actual project looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bot/
│
├── .env
├── package.json
└── src/
    └── services/
        └── test-SFP.js
        └── FlutterwaveClient.js

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

&lt;/div&gt;



&lt;p&gt;In other words, &lt;strong&gt;dotenv&lt;/strong&gt; wasn't failing to read my  *&lt;em&gt;.env *&lt;/em&gt; file.&lt;/p&gt;

&lt;p&gt;It was simply looking in a different location than I expected.&lt;/p&gt;

&lt;p&gt;Once that clicked, everything else started making sense.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two Ways to Fix It
&lt;/h3&gt;

&lt;p&gt;Once I understood the problem, I realized there were two perfectly valid solutions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: Run the application from the project root
&lt;/h3&gt;

&lt;p&gt;Instead of running the test script from &lt;strong&gt;src/services&lt;/strong&gt; like I had been doing, I naviagted to the project root - &lt;strong&gt;bot&lt;/strong&gt; and executed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;bot
node src/services/test-SFP.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the Current Working Directory became:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bot/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;which is exactly where my  &lt;strong&gt;.env&lt;/strong&gt; file lived.&lt;/p&gt;

&lt;p&gt;Without changing a single line of code, dotenv.config() worked exactly as intended.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Tell dotenv exactly where the file is
&lt;/h3&gt;

&lt;p&gt;Alternatively, &lt;strong&gt;dotenv&lt;/strong&gt; allows you to override its default behavior by specifying the location of the  &lt;strong&gt;.env&lt;/strong&gt; file explicitly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dotenv&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;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../.env&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I tried this, I ran my script from inside &lt;strong&gt;src/services.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;src/services
node test-SFP.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Current Working Directory remained:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bot/src/services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this time, &lt;strong&gt;dotenv&lt;/strong&gt; ignored its default lookup location because I had explicitly told it where to find the configuration file.&lt;/p&gt;

&lt;p&gt;Both approaches solved the problem.&lt;/p&gt;

&lt;p&gt;The important lesson was understanding &lt;strong&gt;why&lt;/strong&gt; the default behavior had failed in the first place.&lt;/p&gt;

&lt;p&gt;Once I understood that dotenv.config() uses the application's Current Working Directory by default, both solutions felt perfectly logical instead of feeling like random fixes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Makes Sense
&lt;/h3&gt;

&lt;p&gt;After understanding the behavior, I actually found it quite reasonable.&lt;/p&gt;

&lt;p&gt;Project-wide configuration files such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;dotenv&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;package.json&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;tsconfig.json etc&lt;/em&gt; typically live in the project’s root directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It makes sense for libraries like &lt;strong&gt;dotenv&lt;/strong&gt; and the others mentioned to assume that the application starts from the project root.&lt;/p&gt;

&lt;p&gt;That way, every file in the project shares the same configuration regardless of where the file itself lives.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lessons For The Future
&lt;/h3&gt;

&lt;p&gt;This experience left me with a few lessons that probably will remember for a long time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. A script’s location is not the same as the application’s Current Working Directory.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Those are two different concepts, and they affect how many libraries resolve files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Error messages often tell you more than you initially realize.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The error wasn’t just saying that  &lt;strong&gt;.env&lt;/strong&gt; couldn't be found.&lt;/p&gt;

&lt;p&gt;It was telling me exactly where it had looked.&lt;/p&gt;

&lt;p&gt;I simply hadn’t paid enough attention to that detail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Stop guessing. Start gathering evidence.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The turning point wasn’t another code change.&lt;/p&gt;

&lt;p&gt;It was inspecting the output of &lt;strong&gt;dotenv.config()&lt;/strong&gt; and allowing the error message to guide the investigation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Understanding the runtime is just as important as writing code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This wasn’t really a *&lt;em&gt;dotenv *&lt;/em&gt; problem.&lt;/p&gt;

&lt;p&gt;It was a Node.js concept that I hadn’t fully understood yet.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>softwaredevelopment</category>
      <category>node</category>
      <category>backenddevelopment</category>
    </item>
    <item>
      <title>A UUID Lesson in Laravel</title>
      <dc:creator>Mzer Terdoo</dc:creator>
      <pubDate>Sat, 25 Jul 2026 22:55:06 +0000</pubDate>
      <link>https://dev.to/terdoo-mzer/a-uuid-lesson-in-laravel-5ia</link>
      <guid>https://dev.to/terdoo-mzer/a-uuid-lesson-in-laravel-5ia</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fybc8onv3hjbc5ysnkr6x.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fybc8onv3hjbc5ysnkr6x.png" alt="laravel uuid lesson" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;em&gt;Lesons learned in Laravel while building&lt;/em&gt; &lt;strong&gt;&lt;em&gt;Swyftride -  &lt;/em&gt;&lt;/strong&gt; &lt;em&gt;a WhatsApp bus-booking system&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;Early in the development of &lt;strong&gt;&lt;em&gt;Swyftride&lt;/em&gt;&lt;/strong&gt; , I decided every table would use UUIDs instead of auto-incrementing integers, generated at the database level. See the logic I used to generate uuids in my migration file below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;primary&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;default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gen_random_uuid()'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This worked for the most part until until trips creation actually revealed something I missed earlier. This broke the app in way, and I had to rethink this approach to one that truly served the purpose I had in mind.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;The bug&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Each time the I created a trip, right in the transaction, &lt;strong&gt;&lt;em&gt;n&lt;/em&gt;&lt;/strong&gt; number of seats needed to be generated based on the capacity of the bus assigned to that trip. See the code snippet below showing the DB transaction responsible for this :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$validatedTripData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$validatedTripData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'trips'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$tripData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$trip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Trip&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nv"&gt;$tripData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'capacity'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Seat&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'trip_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$trip&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'seat_number'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'status'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'available'&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;Every seat insert into the &lt;strong&gt;seats&lt;/strong&gt; table failed since &lt;strong&gt;$trip-&amp;gt;id&lt;/strong&gt; was &lt;strong&gt;null. $trip&lt;/strong&gt; held every attribute I had set explicitly. Only &lt;strong&gt;$trip-&amp;gt;id&lt;/strong&gt; was missing. While a trip was indeed created, and a UUID sat in the &lt;strong&gt;trips&lt;/strong&gt; table, my php program knew nothing about this UUID that was created in the &lt;strong&gt;id column&lt;/strong&gt; at the database level.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;What happened?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Model::create()&lt;/strong&gt; only auto-populates a DB-generated key when the model is &lt;strong&gt;incrementing = true&lt;/strong&gt; that path calls &lt;strong&gt;insertGetId()&lt;/strong&gt;, which on Postgres compiles to &lt;strong&gt;INSERT ... RETURNING id.&lt;/strong&gt; For a non-incrementing key, &lt;strong&gt;create() j&lt;/strong&gt;ust inserts and stops.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Fix I discovered
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Database\Eloquent\Concerns\HasUuids&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Trip&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Model&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;HasUuids&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$trip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Trip&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$trip&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// "018f2b5c-6a7f-7b12-9d6f-2f8a4e0c9c11"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Illuminate\Database\Eloquent\Concerns\HasUuids&lt;/strong&gt; trait on the model generates the UUID in PHP before the INSERT is built, via Eloquent's &lt;strong&gt;creating&lt;/strong&gt; event. According to Laravel’s official documentation, “&lt;a href="https://laravel.com/docs/13.x/eloquent#primary-keys:~:text=By%20default%2C%20the,for%20your%20models." rel="noopener noreferrer"&gt;&lt;em&gt;By default, the&lt;/em&gt;&lt;/a&gt;&lt;a href="https://laravel.com/docs/13.x/eloquent#primary-keys:~:text=By%20default%2C%20the,for%20your%20models." rel="noopener noreferrer"&gt;&lt;em&gt;HasUuids trait will generate UUIDv7 identifiers for your models&lt;/em&gt;&lt;/a&gt;”. What this then means isPostgres never has to invent anything since PHP already handed it a value, and because PHP generated it, PHP already knows it. &lt;strong&gt;$trip-&amp;gt;id&lt;/strong&gt; is populated the instant &lt;strong&gt;create()&lt;/strong&gt;returns. Seat creation worked immediately after.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;One side effect:&lt;/strong&gt; my &lt;strong&gt;DEFAULT gen_random_uuid()&lt;/strong&gt; at the schema level now never fires since PHP always supplies the value first. Not wrong, just dead weight and needed a cleanup.&lt;/p&gt;
&lt;h4&gt;
  
  
  What I could have done otherwise?
&lt;/h4&gt;

&lt;p&gt;The &lt;strong&gt;HasUuids&lt;/strong&gt; wasn’t the only fix I could have utilised. I could have kept the DB-level default and just asked Postgres to return the value it generated by utlising a different approach &lt;strong&gt;insertGetId([…]) — the query builder method&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$tripId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'trips'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;insertGetId&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// line 1&lt;/span&gt;
&lt;span class="nv"&gt;$trip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Trip&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$tripId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// line 2. check the inserted trip exists&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;insertGetId()&lt;/strong&gt; on the query builder effectively returns the id of the insert. Conceptually, it runs this under the hood:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;trips&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;departure_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;price&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="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'lagos'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'abuja'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'2026-07-25 22:00:00'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;15000&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'active'&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;RETURNING&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;Returning id&lt;/strong&gt; is the id of the inserted record. However I had key other features I needed that eloquent provided -  &lt;strong&gt;$fillable&lt;/strong&gt; attribute to protect against mass assignment, and also attribute casting.&lt;/p&gt;

&lt;h4&gt;
  
  
  The mental model going forward
&lt;/h4&gt;

&lt;p&gt;This wasn’t a framework ambush. It was solving a problem at the wrong layer before checking whether Laravel already had an idiom for it. Database-level UUID generation isn’t a mistake in the abstract . The actual gap was narrower: not knowing that &lt;strong&gt;create()&lt;/strong&gt; has no &lt;strong&gt;Returning&lt;/strong&gt; equivalent for non-incrementing keys, and that Laravel's answer is a trait you opt into per model, not a schema default you set once.&lt;/p&gt;

&lt;p&gt;The question I now ask earlier, before writing custom logic at any layer: &lt;strong&gt;does the framework already have an idiomatic way to express this - and does that idiom actually fit what the rest of my system needs, or just what this one model needs?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>laravelframework</category>
      <category>laraveldevelopment</category>
      <category>webdev</category>
      <category>backenddevelopment</category>
    </item>
  </channel>
</rss>
