<?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: nareshipme</title>
    <description>The latest articles on DEV Community by nareshipme (@nareshipme).</description>
    <link>https://dev.to/nareshipme</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%2F3824471%2F4940ab6f-46ad-4ee3-a988-83f89f976689.png</url>
      <title>DEV Community: nareshipme</title>
      <link>https://dev.to/nareshipme</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nareshipme"/>
    <language>en</language>
    <item>
      <title>Fixing "No Credentials Found" when using AWS SSO Profiles in Zsh</title>
      <dc:creator>nareshipme</dc:creator>
      <pubDate>Mon, 27 Apr 2026 05:50:42 +0000</pubDate>
      <link>https://dev.to/nareshipme/fixing-no-credentials-found-when-using-aws-sso-profiles-in-zsh-3ia7</link>
      <guid>https://dev.to/nareshipme/fixing-no-credentials-found-when-using-aws-sso-profiles-in-zsh-3ia7</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; An AWS SSO login was successful, but subsequent AWS commands failed with "No credentials found" because the shell alias was pointing to a profile name that didn't match the configured credential block in &lt;code&gt;~/.aws/config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I wanted to automate my AWS authentication by creating an alias in my &lt;code&gt;.zshrc&lt;/code&gt; to trigger the SSO login process. After running the command, the terminal reported &lt;code&gt;Login successful&lt;/code&gt;, but any subsequent attempt to list S3 buckets or use the AWS CLI resulted in a credential error.&lt;/p&gt;

&lt;p&gt;The issue stemmed from a mismatch between how the profile was defined in &lt;code&gt;~/.aws/config&lt;/code&gt; and how I was calling it via the CLI. &lt;/p&gt;

&lt;p&gt;Here is the configuration block I had in my &lt;code&gt;~/.aws/config&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[profile 123456789012_AWSEngineerAccessRole]&lt;/span&gt;
&lt;span class="py"&gt;sso_start_url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;https://your-org.awsapps.com/start&lt;/span&gt;
&lt;span class="py"&gt;sso_region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;eu-west-2&lt;/span&gt;
&lt;span class="py"&gt;sso_account_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;123456789012&lt;/span&gt;
&lt;span class="py"&gt;sso_role_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;AWSEngineerAccessRole&lt;/span&gt;
&lt;span class="py"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;eu-west-2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I ran my login command, 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;Login successful
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, running a basic AWS command immediately after produced this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unable to locate credentials. You can configure credentials manually via the 'aws configure' command.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The failure happened because even though the SSO session was active in the background, the AWS CLI does not automatically know which profile to use for subsequent commands unless explicitly instructed. If my alias or environment variable was pointing to a generic &lt;code&gt;default&lt;/code&gt; profile, the CLI looked for credentials under &lt;code&gt;[default]&lt;/code&gt;, found nothing, and failed—ignoring the fact that a valid SSO session existed for the specific &lt;code&gt;123456789012_AWSEngineerAccessRole&lt;/code&gt; profile.&lt;/p&gt;

&lt;p&gt;To fix this, I updated my &lt;code&gt;.zshrc&lt;/code&gt; alias to ensure every command explicitly references the correct profile name defined in the config file:&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="c"&gt;# Before (failed because it defaulted to 'default' profile)&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;awslogin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"aws sso login"&lt;/span&gt;

&lt;span class="c"&gt;# After (explicitly targets the SSO profile)&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;awslogin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"aws sso login --profile 123456789012_AWSEngineerAccessRole"&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'aws --profile 123456789012_AWSEngineerAccessRole'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By appending &lt;code&gt;--profile 123456789012_AWSEngineerAccessRole&lt;/code&gt; to the login command, the CLI maps the successful authentication token specifically to that profile block. By aliasing &lt;code&gt;aws&lt;/code&gt; itself, I ensure all subsequent commands bypass the empty &lt;code&gt;[default]&lt;/code&gt; profile and use the authenticated SSO session.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>zsh</category>
      <category>devops</category>
      <category>cli</category>
    </item>
    <item>
      <title>Why We Switched to Streaming Frame Extraction for Mobile Video Editing</title>
      <dc:creator>nareshipme</dc:creator>
      <pubDate>Fri, 24 Apr 2026 15:32:19 +0000</pubDate>
      <link>https://dev.to/nareshipme/why-we-switched-to-streaming-frame-extraction-for-mobile-video-editing-3cea</link>
      <guid>https://dev.to/nareshipme/why-we-switched-to-streaming-frame-extraction-for-mobile-video-editing-3cea</guid>
      <description>&lt;p&gt;When we first started building ClipCrafter's browser-based video engine, our biggest enemy wasn’t the complexity of FFmpeg—it was something much more silent and deadly: &lt;strong&gt;Memory Exhaustion.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you are working with high-resolution clips or long durations in a Web Worker, there is an invisible wall you will eventually hit. We call it "The OOM (Out Of Render) Wall." &lt;/p&gt;

&lt;p&gt;Recently, we noticed that while our desktop users were enjoying smooth multi-clip stitching, mobile Chrome and Safari users on mid-range Androids/iPhones were experiencing frequent tab crashes during the rendering process. The culprit? Our frame extraction logic was attempting to hold too much in memory at once.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: $O(n)$ Memory Complexity
&lt;/h3&gt;

&lt;p&gt;In our early architecture of &lt;code&gt;framewebworker&lt;/code&gt;, we used a pattern that looked like this conceptually (simplified for clarity):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ THE OLD WAY: High risk of OOM crashes on mobile&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;extractFramesAllAtOnce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoSource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;frames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// This array grows linearly with video length!&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// We were grabbing a frame, converting it to an ImageBitmap/Blob...&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;captureFrameAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentTime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;frames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Every single frame stays in RAM until the end&lt;/span&gt;
    &lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;frames&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// If you have 500 frames of a long clip? Goodbye memory!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this "Load-All" approach, our memory complexity was $O(n)$, where $n$ is total number of extracted frames. For an 8MB video file might be fine—but for high-bitrate clips or longer sequences involving multiple stitches, the browser's heap would balloon until it hit its limit and killed the worker thread (and usually our entire app tab).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: Streaming Extraction
&lt;/h3&gt;

&lt;p&gt;To fix this, we upgraded &lt;code&gt;framewebworker&lt;/code&gt; to version 0.4.0 with a focus on &lt;strong&gt;Streaming Frame Extraction&lt;/strong&gt;. Instead of accumulating an array of frames in memory before starting the stitch process, we moved toward $O(1)$ spatial complexity relative to total frame count (per step).&lt;/p&gt;

&lt;p&gt;We refactored our pipeline so that each extracted frame is processed through its next stage immediately—whether it's being passed into a WebCodecs encoder or written as part of an intermediate stream. &lt;/p&gt;

&lt;p&gt;Here’s how we restructured the logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ THE NEW WAY: Streaming approach (O(1) memory footprint per step)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processFramesStreaming&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoSource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onFrameReady&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Callback&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Blob&lt;/span&gt;&lt;span class="o"&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// target fps&lt;/span&gt;

  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Extract ONLY the current frame needed for this specific timestamp&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;singleFrameBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;captureSingleFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentTime&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Instead of pushing to a massive global array 'frames', 
     * we emit it immediately. The downstream consumer (WebCodecs) 
     * processes the frame and then allows this buffer to be garbage collected.
     */&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;onFrameReady&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;singleFrameBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;currentTime&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why This Matters for Developers Building Heavy Web Apps
&lt;/h3&gt;

&lt;p&gt;By moving from an "Accumulate-then-Process" model to a &lt;strong&gt;Streaming&lt;/strong&gt; model, we achieved two critical wins:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Memory Stability:&lt;/strong&gt; The memory footprint of our worker is now tied more closely to the size of &lt;em&gt;one single frame&lt;/em&gt; rather than total video duration/frame count. This makes mobile rendering significantly more reliable even on low-end devices with restricted heap sizes.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Time To First Byte (TTFB) for Rendering:&lt;/strong&gt; Because we start processing frames as soon as they are extracted, our downstream encoders can begin working immediately without waiting for the "extraction phase" to finish entirely.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Takeaway
&lt;/h3&gt;

&lt;p&gt;When building resource-intensive features in JavaScript—whether it’s video editing with FFmpeg/WebCodecs or heavy data visualization—always ask yourself: &lt;strong&gt;Is my memory usage $O(n)$?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;If your app's stability degrades as the input size increases, you don't have a logic bug; you have an architectural bottleneck. Switching to streaming-based processing is often more difficult than simple array manipulation (due to managing backpressure and state), but it’s what makes "pro" web tools possible on mobile browsers.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;ClipCrafter continues to evolve! Check out our latest updates for even faster WebCodecs hardware acceleration.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>performance</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Fixing "No Credentials Found" when using AWS SSO Profiles in Zsh</title>
      <dc:creator>nareshipme</dc:creator>
      <pubDate>Fri, 24 Apr 2026 06:31:24 +0000</pubDate>
      <link>https://dev.to/nareshipme/fixing-no-credentials-found-when-using-aws-sso-profiles-in-zsh-3ogp</link>
      <guid>https://dev.to/nareshipme/fixing-no-credentials-found-when-using-aws-sso-profiles-in-zsh-3ogp</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; An AWS CLI alias was failing with "No credentials found" because the shell command was not explicitly pointing to the specific named profile configured for SSO. I fixed this by appending &lt;code&gt;--profile&lt;/code&gt; to the command execution within the Zsh alias.&lt;/p&gt;

&lt;p&gt;I set up a new Zsh alias to automate my AWS SSO login process and update my local &lt;code&gt;~/.aws/credentials&lt;/code&gt; file. The configuration in my &lt;code&gt;~/ .aws/config&lt;/code&gt; looked correct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[profile 123456789012_AWSEngineerAccessRole]&lt;/span&gt;
&lt;span class="py"&gt;sso_start_url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;https://your-org.awsapps.com/start&lt;/span&gt;
&lt;span class="py"&gt;sso_region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;eu-west-2&lt;/span&gt;
&lt;span class="py"&gt;sso_account_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;123456789012&lt;/span&gt;
&lt;span class="py"&gt;sso_role_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;AWSEngineerAccessRole&lt;/span&gt;
&lt;span class="py"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;eu-west-2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, when running my custom login alias, the CLI returned this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error: No credentials found in the configured profile.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The issue was that &lt;code&gt;aws sso login&lt;/code&gt; does not automatically assume you want to use a specific named profile if it isn't the &lt;code&gt;[default]&lt;/code&gt; profile. If your configuration uses a custom profile name like &lt;code&gt;123456789012_AWSEngineerAccessRole&lt;/code&gt;, running a bare &lt;code&gt;aws sso login&lt;/code&gt; command causes the CLI to look for credentials under the &lt;code&gt;[default]&lt;/code&gt; block. Since that block was empty or missing the SSO metadata, it failed immediately.&lt;/p&gt;

&lt;p&gt;To fix this, I updated my &lt;code&gt;.zshrc&lt;/code&gt; alias to explicitly reference the profile name using the &lt;code&gt;--profile&lt;/code&gt; flag:&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="c"&gt;# Before (Failing)&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;awslogin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"aws sso login"&lt;/span&gt;

&lt;span class="c"&gt;# After (Working)&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;awslogin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"aws sso login --profile 123456789012_AWSEngineerAccessRole"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding &lt;code&gt;--profile&lt;/code&gt;, the AWS CLI knows exactly which block in &lt;code&gt;~/.aws/config&lt;/code&gt; to parse for the &lt;code&gt;sso_start_url&lt;/code&gt; and &lt;code&gt;sso_account_id&lt;/code&gt;. Now, running &lt;code&gt;awslogin&lt;/code&gt; triggers the browser authentication flow and correctly maps the session tokens to that specific profile.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cli</category>
      <category>zsh</category>
      <category>devops</category>
    </item>
    <item>
      <title>The domain name was just productive procrastination</title>
      <dc:creator>nareshipme</dc:creator>
      <pubDate>Thu, 23 Apr 2026 15:54:37 +0000</pubDate>
      <link>https://dev.to/nareshipme/the-domain-name-was-just-productive-procrastination-16j2</link>
      <guid>https://dev.to/nareshipme/the-domain-name-was-just-productive-procrastination-16j2</guid>
      <description>&lt;p&gt;I finally reached a point with ClipCrafter where I had nothing left to hide behind in the code. &lt;/p&gt;

&lt;p&gt;The features were all there—authentication flows working perfectly, transcription logic solid, clip generation functional, and even an export system that didn't crash every five minutes. After about 238 pull requests, it wasn’t just a "cool experiment" anymore; it was actually running like a real product. But despite having the tech ready to go, I kept delaying one specific thing: getting a domain name.&lt;/p&gt;

&lt;p&gt;There is something deeply uncomfortable about attaching your own URL to an unfinished idea. As long as everything lived on some random deployment sub-domain or localhost, any bugs were just "developer quirks." Once you buy &lt;code&gt;something.com&lt;/code&gt;, the project becomes real. It’s no longer yours alone; it belongs to whoever finds that link in a Google search and decides whether they like what they see.&lt;/p&gt;

&lt;p&gt;When I finally forced myself to face this hurdle last week, I hit an immediate wall of frustration. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clipcrafter.com? Taken.&lt;/li&gt;
&lt;li&gt;clipcrafter.app? Taken.&lt;/li&gt;
&lt;li&gt;clipcrafter.io? Taken.&lt;/li&gt;
&lt;li&gt;clipcrafter.video? Also taken.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I even had a brief moment where I considered just using my personal brand domain, &lt;code&gt;kshan.ai&lt;/code&gt;. It felt safe because it was already mine and nothing could change with me personally failing at this project—but that would have been dishonest to the product itself. ClipCrafter needed its own identity. &lt;/p&gt;

&lt;p&gt;Instead of spiraling into a naming crisis, I decided to stop manual searching and wrote a quick script to check availability for different prefixes across various TLDs programmatically. It was much less emotional than clicking through domain registrars one by one. That’s when I found &lt;code&gt;getclipcrafter.com&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;It wasn't "perfect," but it followed the classic indie hacker playbook—the same pattern used by giants like Postman (&lt;code&gt;getpostman.com&lt;/code&gt;) or Figma (before they moved to their primary). It was cheap, available, and descriptive enough that people would know exactly what I do. Within minutes of buying it, my DNS records were updated and pointed at a live production deployment.&lt;/p&gt;

&lt;p&gt;Looking back on the last few days spent obsessing over TLDs ($com vs $io) or prefix choices (get- vs try-) was actually quite revealing to me. The domain research wasn't about branding; it was productive procrastination. I used "finding the perfect name" as a shield against launching because once that URL is live, you can no longer hide from feedback.&lt;/p&gt;

&lt;p&gt;The fear of someone using the tool and finding it broken or useless became much more concrete the moment &lt;code&gt;getclipcrafter.com&lt;/code&gt; appeared in my browser bar. But strangely enough, making that fear real made it easier to face. You can't fix a product if nobody is looking at you actually ship it.&lt;/p&gt;

&lt;p&gt;The app is officially live now. No more excuses—time to find those first users and see what they think of the mess I’ve built.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>entrepreneurship</category>
      <category>buildinginpublic</category>
      <category>indiehackers</category>
    </item>
    <item>
      <title>When Turbopack Breaks Your Web Worker Builds</title>
      <dc:creator>nareshipme</dc:creator>
      <pubDate>Thu, 23 Apr 2026 15:32:10 +0000</pubDate>
      <link>https://dev.to/nareshipme/when-turbopack-breaks-your-web-worker-builds-1ahe</link>
      <guid>https://dev.to/nareshipme/when-turbopack-breaks-your-web-worker-builds-1ahe</guid>
      <description>&lt;p&gt;In the world of modern frontend development, "speed" is usually a good thing. We want faster hot reloads during development and lightning-fast build times for production. &lt;/p&gt;

&lt;p&gt;Recently at ClipCrafter, we hit this wall head-on when upgrading to Next.js 16. While moving towards Turbopack promised us incredible developer experience improvements, it introduced a silent killer: our video processing engine stopped working in production builds because of how Webpack handles worker files differently than Turbo/Turbopack does during the build pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: A Broken Worker Pipeline
&lt;/h3&gt;

&lt;p&gt;ClipCrafter relies heavily on &lt;code&gt;@ffmpeg/ffmpeg&lt;/code&gt; to handle heavy-duty video manipulation directly in the browser. This requires loading a specific &lt;code&gt;.wasm&lt;/code&gt; file and an ESM worker via a Blob URL at runtime. &lt;/p&gt;

&lt;p&gt;To make this work, we had configured a custom Webpack loader in &lt;code&gt;next.config.ts&lt;/code&gt;. We needed specifically told webpack how to treat these files so they wouldn't be mangled during the bundling process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// next.config.ts (The "Old" Way that worked)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isServer&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isServer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// We needed this custom loader to ensure ffmpeg-core &lt;/span&gt;
      &lt;span class="c1"&gt;// could be resolved correctly via Blob URLs at runtime.&lt;/span&gt;
      &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kr"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/ffmpeg&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;dist&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;esm&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;worker&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;js$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;url-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Or specialized worker loaders&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we upgraded, Next.js started defaulting to Turbopack for builds in certain environments. Since our configuration was strictly targeting the &lt;code&gt;webpack&lt;/code&gt; object and didn't provide a corresponding Turbo-compatible instruction set via &lt;code&gt;turbo: { rules: ... }&lt;/code&gt;, those critical worker files were being bundled incorrectly. The result? A production build that looked perfect but crashed with an "Uncaught TypeError" as soon as you tried to initialize any video clip processing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Moving Toward Hardware Acceleration
&lt;/h3&gt;

&lt;p&gt;While fixing the bundler, we also had a chance to look at our performance bottleneck. We realized &lt;code&gt;ffmpeg.wasm&lt;/code&gt; was great for compatibility, but it's essentially running software-based encoding in JavaScript—which is slow and heavy on CPU usage. &lt;/p&gt;

&lt;p&gt;We decided to upgrade/integrate with &lt;code&gt;@framewebworker@0.3.0&lt;/code&gt;. This library uses &lt;strong&gt;WebCodecs&lt;/strong&gt;, a modern browser API that allows us to tap into the device’s hardware acceleration (H.264). The difference was night and day: we saw encoding speeds jump from "painfully slow" up to 10–50× faster on supported browsers, with an automatic fallback to our existing FFmpeg setup for older devices.&lt;/p&gt;

&lt;h3&gt;
  
  
  How We Fixed It
&lt;/h3&gt;

&lt;p&gt;To get the build pipeline stable again while embracing these upgrades, we had two main tasks in a single PR:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Force Webpack for Production:&lt;/strong&gt; Until Next.js provides more robust Turbopack configuration hooks specifically for complex WASM/Worker loaders like ours, we explicitly forced &lt;code&gt;webpack&lt;/code&gt; during our production builds to ensure the &lt;code&gt;@ffmpeg/ffmpeg&lt;/code&gt; worker remains intact and resolvable at runtime. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Clean up Route Exports:&lt;/strong&gt; We also had a minor type-safety issue where arbitrary constants (like language lists) were being exported from Next.js App Router files, causing TypeScript violations in generated types during build time. In &lt;code&gt;route.ts&lt;/code&gt;, you should &lt;em&gt;only&lt;/em&gt; export HTTP handlers (&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;) and config objects.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Lesson Learned
&lt;/h3&gt;

&lt;p&gt;If your application relies on "heavy" browser APIs like WebAssembly (WASM), SharedArrayBuffer, or complex Worker threads: &lt;strong&gt;Don't assume the new default bundler just works.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;The transition from Webpack to Turbopack is a massive leap forward for dev speed, but it requires you to audit how your most critical pieces of infrastructure—the ones that don't follow standard "UI component" rules—are being bundled. We fixed our build by reverting the production engine back to its proven Webpack configuration while keeping all other Next.js 16 benefits intact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have you run into bundling issues with newer versions of Next.js? Let us know in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>typescript</category>
      <category>webassembly</category>
      <category>websockets</category>
    </item>
    <item>
      <title>Debugging "No Credentials Found" when Aliasing AWS SSO Login in ZSH</title>
      <dc:creator>nareshipme</dc:creator>
      <pubDate>Thu, 23 Apr 2026 12:13:53 +0000</pubDate>
      <link>https://dev.to/nareshipme/debugging-the-ghost-credentials-when-your-cli-says-one-thing-and-your-config-says-another-4kf4</link>
      <guid>https://dev.to/nareshipme/debugging-the-ghost-credentials-when-your-cli-says-one-thing-and-your-config-says-another-4kf4</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Creating a ZSH alias that runs &lt;code&gt;aws sso login&lt;/code&gt; appeared to succeed, but subsequent commands failed with "no credentials found" because the alias was not correctly setting the profile for downstream tools.&lt;/p&gt;

&lt;p&gt;I was trying to streamline my workflow by adding an alias to my &lt;code&gt;.zshrc&lt;/code&gt; to automate logging into a specific AWS SSO profile. The goal was simple: run one command, and have all subsequent AWS CLI commands work immediately using that authenticated session.&lt;/p&gt;

&lt;p&gt;The alias looked like this in my &lt;code&gt;.zshrc&lt;/code&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;alias &lt;/span&gt;&lt;span class="nv"&gt;awslogin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"aws sso login --profile 123456789012_AWSEngineerAccessRole"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I ran &lt;code&gt;awslogin&lt;/code&gt;, the terminal returned:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;However, as soon as I tried to list my S3 buckets using that same profile, the CLI threw a credential error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fatal error: An error occurred (NoCredentialsError) when calling the ListBuckets operation: unable to locate credentials.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Root Cause
&lt;/h3&gt;

&lt;p&gt;The issue was not with the SSO login itself — the browser-based authentication was completing successfully and updating the token cache in &lt;code&gt;~/.aws/sso/cache&lt;/code&gt;. The problem was a mismatch between how I was initiating the session and how my AWS configuration was structured.&lt;/p&gt;

&lt;p&gt;In my &lt;code&gt;~/.aws/config&lt;/code&gt;, I had defined the profile like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[profile 123456789012_AWSEngineerAccessRole]&lt;/span&gt;
&lt;span class="py"&gt;sso_start_url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;https://your-org.awsapps.com/start&lt;/span&gt;
&lt;span class="py"&gt;sso_region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;eu-west-2&lt;/span&gt;
&lt;span class="py"&gt;sso_account_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;123456789012&lt;/span&gt;
&lt;span class="py"&gt;sso_role_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;AWSEngineerAccessRole&lt;/span&gt;
&lt;span class="py"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;eu-west-2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While &lt;code&gt;aws sso login --profile &amp;lt;name&amp;gt;&lt;/code&gt; successfully refreshed the SSO token, subsequent commands were failing because they were not explicitly told to use that specific profile. The AWS CLI defaults to looking for a &lt;code&gt;[default]&lt;/code&gt; profile or &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; environment variables. Since neither was set, it found nothing — even though the SSO token was valid.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fix
&lt;/h3&gt;

&lt;p&gt;Update the alias to also export &lt;code&gt;AWS_PROFILE&lt;/code&gt; after a successful login:&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;alias &lt;/span&gt;&lt;span class="nv"&gt;awslogin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'aws sso login --profile 123456789012_AWSEngineerAccessRole &amp;amp;&amp;amp; export AWS_PROFILE=123456789012_AWSEngineerAccessRole'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; means the export only runs if the login succeeded. From that point, every subsequent command in the shell session picks up the correct profile automatically — no &lt;code&gt;--profile&lt;/code&gt; flag needed.&lt;/p&gt;

&lt;p&gt;Verification:&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="nv"&gt;$ &lt;/span&gt;awslogin
Login successful
&lt;span class="nv"&gt;$ &lt;/span&gt;aws s3 &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;your buckets appear]
&lt;span class="nv"&gt;$ &lt;/span&gt;aws sts get-caller-identity
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"UserId"&lt;/span&gt;: &lt;span class="s2"&gt;"..."&lt;/span&gt;,
    &lt;span class="s2"&gt;"Account"&lt;/span&gt;: &lt;span class="s2"&gt;"123456789012"&lt;/span&gt;,
    &lt;span class="s2"&gt;"Arn"&lt;/span&gt;: &lt;span class="s2"&gt;"arn:aws:sts::123456789012:assumed-role/AWSEngineerAccessRole/..."&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;sts get-caller-identity&lt;/code&gt; check is worth adding to your alias or running manually after login — it confirms the session is actually active, not just that the token handshake completed.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>zsh</category>
      <category>devops</category>
      <category>cli</category>
    </item>
    <item>
      <title>Why We Switched from Stripe to Razorpay for ClipCrafter’s Billing Engine</title>
      <dc:creator>nareshipme</dc:creator>
      <pubDate>Wed, 22 Apr 2026 14:01:20 +0000</pubDate>
      <link>https://dev.to/nareshipme/why-we-switched-from-stripe-to-razorpay-for-clipcrafters-billing-engine-331o</link>
      <guid>https://dev.to/nareshipme/why-we-switched-from-stripe-to-razorpay-for-clipcrafters-billing-engine-331o</guid>
      <description>&lt;p&gt;Building a SaaS is rarely just about the core feature. For us at ClipCrafter—an AI-powered video editor—the "video" part was actually one of our easier challenges. The real complexity crept in when we had to figure out how to charge people for it.&lt;/p&gt;

&lt;p&gt;Recently, we hit a major milestone: moving from an experimental Stripe setup into a production-ready billing system powered by Razorpay. While many developers default to Stripe because of its incredible documentation and global footprint, scaling ClipCrafter required us to rethink our payment architecture based on specific regional needs and usage enforcement.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Why" Behind the Pivot
&lt;/h3&gt;

&lt;p&gt;When we first started prototyping Phase 10 (our Billing &amp;amp; Payments phase), a dual-provider approach seemed attractive for international coverage via Stripe + Razorpay. However, as development progressed, it became clear that maintaining two separate webhooks, two different subscription lifec/ycles, and twofold error handling was creating massive technical debt in our Inngest workflows.&lt;/p&gt;

&lt;p&gt;We decided to simplify: &lt;strong&gt;Razorpay only.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;By focusing on a single provider for the initial launch phase (specifically targeting India-based payments), we were able to prune significant amounts of boilerplate code from both our Next.js API routes and our background workers. We removed &lt;code&gt;stripe_subscription_id&lt;/code&gt; entirely, cleaned up our database migrations in Supabase, and focused strictly on usage enforcement via a unified schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing Usage Enforcement
&lt;/h3&gt;

&lt;p&gt;The real challenge wasn't just "taking money"—it was ensuring that if someone is on the 'Starter' plan, they don’t accidentally trigger an unlimited number of high-compute video renders using our WebWorker architecture. &lt;/p&gt;

&lt;p&gt;We needed a way to check usage limits before every single rendering job hit our worker queue via Inngest. Here is how we structured that logic in TypeScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/lib/billing.ts - Simplified Usage Check Logic&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@supabase/supabase-js&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;UserBillingRow&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;planKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;starter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;videoCreditsRemaining&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validateRenderPermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;supabaseClient&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="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Fetch the current user billing status from Supabase&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;billingData&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;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabaseClient&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user_billing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;planKey, videoCreditsRemaining&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;single&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;billingData&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="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;Could not retrieve billing information.&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;planKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;videoCreditsRemaining&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;billingData&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;UserBillingRow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Enforce hard limits based on the Plan Key&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="s2"&gt;`Checking permissions for &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;planKey&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; user...`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoCreditsRemaining&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="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;No credits remaining! Please upgrade your plan.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Permission granted to proceed with rendering task&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Lesson: Pruning is as important as Adding
&lt;/h3&gt;

&lt;p&gt;The most satisfying part of this refactor wasn't adding the Razorpay integration—it was deleting everything related to Stripe. We removed entire client libraries, deleted webhook listeners that were no longer being triggered, and cleaned up our &lt;code&gt;PlanBadge&lt;/code&gt; components which previously had "zombie" styles for plans we weren't even supporting anymore via a specific provider.&lt;/p&gt;

&lt;p&gt;In software engineering (and especially in early-stage startups), there is an immense temptation to build every possible integration from Day 1 just because you &lt;em&gt;might&lt;/em&gt; need it later. We learned that the complexity of managing two payment gateways far outweighed any potential benefit for our current user base.&lt;/p&gt;

&lt;h3&gt;
  
  
  Takeaway
&lt;/h3&gt;

&lt;p&gt;If you're building a global SaaS, don't let "feature creep" infect your core infrastructure before they even pay their first invoice. Pick one provider, master its webhook lifecycle and usage enforcement patterns (like we did with Inngest + Supabase), and only expand when the market—not your imagination—demands it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What’s your experience with payment gateways? Did you stick to Stripe or find a more localized solution for much of less friction? Let us know in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>nextjs</category>
      <category>razorpay</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Dev Journal: 2026-04-21</title>
      <dc:creator>nareshipme</dc:creator>
      <pubDate>Tue, 21 Apr 2026 06:42:21 +0000</pubDate>
      <link>https://dev.to/nareshipme/dev-journal-2026-04-21-1iei</link>
      <guid>https://dev.to/nareshipme/dev-journal-2026-04-21-1iei</guid>
      <description>&lt;p&gt;/Users/ONBAdmin/Development/term-sheet/scripts/daily-blog.sh: line 132: /users/ONBAdmin/.nvm/versions/node/v20.19.0/bin/claude: No such file or directory&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Dev Journal: 2026-04-21</title>
      <dc:creator>nareshipme</dc:creator>
      <pubDate>Tue, 21 Apr 2026 06:30:08 +0000</pubDate>
      <link>https://dev.to/nareshipme/dev-journal-2026-04-21-4ph</link>
      <guid>https://dev.to/nareshipme/dev-journal-2026-04-21-4ph</guid>
      <description>&lt;p&gt;Not logged in · Please run /login&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Dev Journal: 2026-04-20</title>
      <dc:creator>nareshipme</dc:creator>
      <pubDate>Mon, 20 Apr 2026 05:30:09 +0000</pubDate>
      <link>https://dev.to/nareshipme/dev-journal-2026-04-20-372k</link>
      <guid>https://dev.to/nareshipme/dev-journal-2026-04-20-372k</guid>
      <description>&lt;p&gt;Not logged in · Please run /login&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Dev Journal: 2026-04-17</title>
      <dc:creator>nareshipme</dc:creator>
      <pubDate>Thu, 16 Apr 2026 18:30:02 +0000</pubDate>
      <link>https://dev.to/nareshipme/dev-journal-2026-04-17-4759</link>
      <guid>https://dev.to/nareshipme/dev-journal-2026-04-17-4759</guid>
      <description>&lt;p&gt;env: node: No such file or directory&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Dev Journal: 2026-04-16</title>
      <dc:creator>nareshipme</dc:creator>
      <pubDate>Wed, 15 Apr 2026 18:32:11 +0000</pubDate>
      <link>https://dev.to/nareshipme/dev-journal-2026-04-16-47od</link>
      <guid>https://dev.to/nareshipme/dev-journal-2026-04-16-47od</guid>
      <description>&lt;p&gt;env: node: No such file or directory&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
