<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[My Web Notes]]></title><description><![CDATA[<p>I constantly learn about so many tools, frameworks, tricks, and techniques while building my SaaS apps on the web platform. This blog is basically my brain dump of all that learning. Hope it helps!</p>
]]></description><link>https://mywebnotes.xyz/blog</link><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 07:50:46 GMT</lastBuildDate><atom:link href="https://mywebnotes.xyz/blog/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><atom:link rel="first" href="https://mywebnotes.xyz/blog/rss.xml"/><item><title><![CDATA[Simple yet useful AI prompts for developers using React and Next.js]]></title><description><![CDATA[<p>Here are some of the most common and useful prompts I use with Cursor when working with apps built on React/Next.js. Although I use Cursor for my work, these prompts should work just fine with any AI-powered Code Editor or IDE.</p>
<p>I usually leave Cursors Agent mode with the model setting on Auto, but I felt like these prompts performed better with Sonnet 4.5 as the model, the few times I used it.</p>
<p>Now, on to the prompts.</p>
<h2 id="heading-prompt-1-fixing-build-errors">Prompt #1: Fixing Build Errors.</h2>
<p>The prompt:</p>
<pre><code class="lang-plaintext">Fix all build errors, ignore any warnings, and repeat this until a build is successful. Use: npm run build.
</code></pre>
<blockquote>
<p>I used <code>npm run build</code> but you can write whichever one you use. Also, Cursor usually figures out which one to use.</p>
</blockquote>
<p>This is a very simple prompt. However, this is probably my most used prompt. Every time i ask Cursor to write a feature or an entire application (mostly on top of <a target="_blank" href="https://github.com/devsForFun/starterkit">starterkit template</a> that i use to build my apps), most times, it uses things like <code>any</code> and other errors that you wouldnt notice unless you run your build command because they dont show up in your development server. It think this is a very common problem when youre vibe coding.</p>
<p>It is really boring to try and fix them all by myself. So, once Im satisfied with the state of the applications working, I generally run this prompt and review the changes it made. It is just super convenient to do it like this.</p>
<blockquote>
<p><strong>Note:</strong> The auto mode does just fine for most cases, unless you one shotted a massive application and its not even running properly (in which case, Id suggest using Sonnet 4.5 explicitly).</p>
</blockquote>
<h2 id="heading-prompt-2-generating-a-pr-message">Prompt #2: Generating a PR message</h2>
<p>The prompt:</p>
<pre><code class="lang-plaintext">Generate a technical and concise PR message for the last {x} commits.
</code></pre>
<blockquote>
<p>Replace <code>{x}</code> with the number of commits you want to generate the PR message for.</p>
</blockquote>
<p>I find this useful because sometimes I refer back to my pull requests to see what I did and how I did certain things in the past. Over time, I had multiple variations of this prompt before I finally arrived at this. I know it feels like Im talking too much about a super tiny prompt like this but, believe me when I say it, I used use a prompt that was a bit more detailed than this, and the results were mostly jargon &amp; often times over-estimated than what actually was there.</p>
<p>So, at least in my opinion, this yields a PR message that kinda does a fine job at recording all the changes that were made, described in good developer lingo.</p>
<blockquote>
<p><strong>Note:</strong> Only Sonnet 4.5 Thinking seems to be doing a good job with this prompt. The auto modes results are unpredictable and sometimes not very useful.</p>
</blockquote>
<h2 id="heading-and-thats-it">And thats it!</h2>
<p>Yeah really. I just wanted to leave them here as my notes, and for anyone that comes across this. Figured I might as well hear some thoughts on this. You can reach out to me on X (<a target="_blank" href="https://x.com/CharanMNX">CharanMNX</a>) or on my <a target="_blank" href="http://linkedin.com/in/charan-manikanta">LinkedIn profile</a>. Im happy to chat!</p>
<p>Happy Coding/Vibe-coding!</p>
]]></description><link>https://mywebnotes.xyz/blog/simple-yet-useful-ai-prompts-for-developers-using-react-and-nextjs</link><guid isPermaLink="true">https://mywebnotes.xyz/blog/simple-yet-useful-ai-prompts-for-developers-using-react-and-nextjs</guid><category><![CDATA[#PromptEngineering]]></category><category><![CDATA[vibe coding]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Charan Manikanta Nalla]]></dc:creator></item><item><title><![CDATA[How to Set Up a Local Supabase Instance to Mirror Your Existing Project]]></title><description><![CDATA[<p>Setting up a local instance for your Supabase backend is very important because, without it, youd be experimenting on your live database and that might mess things up for your application, especially if its live. You can use the Supabase CLI to setup up a local instance of your already existing Supabase project, or even create a new one.</p>
<p>Some benefits of setting up a local instance of your Supabase backend are:</p>
<ul>
<li><p>Helps you keep production and development environments separate.</p>
</li>
<li><p>Allows for completely offline development.</p>
</li>
<li><p>You dont really need an online Supabase project, which means, unlimited test projects!</p>
</li>
</ul>
<h2 id="heading-setting-up-supabase-cli">Setting Up Supabase CLI</h2>
<p>If you dont have Supabase CLI setup on your machine and in your project, refer to this post:</p>
<blockquote>
<p><a target="_blank" href="https://www.mywebnotes.xyz/blog/using-supabase-cli-how-to-configure-local-supabase-for-nextjs-development"><strong>Using Supabase CLI: How to Configure Local Supabase for Next.js Development</strong></a></p>
</blockquote>
<p>Although the title says its for Next.js, the instructions are pretty generic and will work for almost all types of projects.</p>
<h2 id="heading-linking-amp-syncing-local-instance-with-the-online-one">Linking &amp; Syncing Local Instance With The Online One</h2>
<p>After youre done with the Supabase CLI setup, follow these instructions to link and sync your Supabase backend with a local instance:</p>
<ol>
<li><p>Login to Supabase using the following command:</p>
<pre><code class="lang-bash"> supabase login
</code></pre>
</li>
<li><p>Link the local project to the one online using the link command and the project id:</p>
<pre><code class="lang-bash"> supabase link --project-ref YOUR_PROJECT_REF_ID
</code></pre>
</li>
<li><p>Next, run the following command:</p>
<pre><code class="lang-bash"> supabase db pull
</code></pre>
<blockquote>
<p>This pulls all the tables, functions, policies, etc., from the online version and generates a migration script under the <code>/supabase/migrations</code> folder.</p>
</blockquote>
<p> Sometimes, unexpected errors may pop up, so will a list of commands along with them, like below:</p>
<pre><code class="lang-bash"> supabase migration repair --status reverted 20250609121341
 supabase migration repair --status reverted 20250609121411
 supabase migration repair --status reverted 20250609121421
 supabase migration repair --status reverted 20250609121430
 supabase migration repair --status reverted 20250609121549
</code></pre>
<p> Just run them in order and run the <code>supabase db pull</code> command again to generate the migration file.</p>
</li>
<li><p>Finally, run the following command to complete setting up your local Supbase backend setup.</p>
<pre><code class="lang-bash"> supabase db reset
</code></pre>
<blockquote>
<ol>
<li><p>No, this wont delete any data on your original database.</p>
</li>
<li><p>This wont bring a copy of data from your original database.</p>
</li>
<li><p>This will setup all the tables, functions, policies, etc., as they were in your original database.</p>
</li>
</ol>
</blockquote>
</li>
</ol>
<p>And thats it! Your Supabase backend now has a local version that you can play with, without worrying about messing up with anything.</p>
<h2 id="heading-bonus">Bonus</h2>
<p>It kinda sucks how AI tools like Cursor, Windsurf, etc., never ask you setup a local instance using Supabase and jump straight to doing things with Supabase online. That is so messed up in many ways. Youd be testing your app and modifying things on a production environment. That is the equivalent of making changes to your app while people are using your app.</p>
<p>That being said, if youre a vibe-coder, or a developer who didnt know that you could do something like this, next time you prompt AI to build an app for you, if youre sure that youll be using Supabase for your database/backend, include this line: <strong>Use Supabase CLI.</strong></p>
<p>Happy Coding and Vibe-Coding!</p>
]]></description><link>https://mywebnotes.xyz/blog/how-to-set-up-a-local-supabase-instance-to-mirror-your-existing-project</link><guid isPermaLink="true">https://mywebnotes.xyz/blog/how-to-set-up-a-local-supabase-instance-to-mirror-your-existing-project</guid><category><![CDATA[supabase]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[cli]]></category><category><![CDATA[version control]]></category><category><![CDATA[Databases]]></category><dc:creator><![CDATA[Charan Manikanta Nalla]]></dc:creator></item><item><title><![CDATA[Using Supabase CLI: How to Configure Local Supabase for Next.js Development]]></title><description><![CDATA[<p>If youre building projects with Supabase, using Supabase CLI is a must. The main benefit of using Supabase CLI is that it lets you setup a local instance of your Supabase backend so that you can play with your database and test it, without worrying about messing up your production Supabase backend. Once setup, you can run everything offline!</p>
<p>Heres how to setup Supabase CLI and use it in your Next.js project.</p>
<ol>
<li><p>Install on your os.</p>
<pre><code class="lang-bash"> <span class="hljs-comment"># macos &amp; linux:</span>
 brew install supabase/tap/supabase

 <span class="hljs-comment"># windows:</span>
 scoop bucket add supabase https://github.com/supabase/scoop-bucket.git
 scoop install supabase
</code></pre>
<blockquote>
<p>Refer to this article for more details: <a target="_blank" href="https://supabase.com/docs/guides/local-development/cli/getting-started">Supabase CLI | Supabase Docs</a></p>
</blockquote>
</li>
<li><p>Make sure to have Docker Desktop installed.</p>
<blockquote>
<p>Refer to this for Docker Desktop if you dont have it installed: <a target="_blank" href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a></p>
</blockquote>
</li>
<li><p>Run the following command in the root folder of your Next.js project.</p>
<pre><code class="lang-bash"> supabase init
</code></pre>
<blockquote>
<p>This will create a folder called <code>supabase</code>. Also a <code>supabase/config.toml</code> file is generated after running <code>supabase init</code>. You can edit this file to change the settings for your locally running project. After you make changes, you will need to restart using <code>supabase stop</code> and then <code>supabase start</code> for the changes to take effect.</p>
</blockquote>
</li>
<li><p>Start Docker Desktop on your machine.</p>
</li>
<li><p>Run this following command</p>
<pre><code class="lang-bash"> supabase start
</code></pre>
<p> And go watch a half/full episode of Naruto or Brooklyn Nine-Nine (cause thats how long this might take, depending on your machine and internet connection, especially when its the first time).</p>
</li>
<li><p>After completing the run, it would give you some credentials like below, in your terminal:</p>
<pre><code class="lang-plaintext">          API URL: http://127.0.0.1:54321
      GraphQL URL: http://127.0.0.1:54321/graphql/v1
   S3 Storage URL: http://127.0.0.1:54321/storage/v1/s3
           DB URL: postgresql://postgres:postgres@127.0.0.1:54322/postgres
       Studio URL: http://127.0.0.1:54323
     Inbucket URL: http://127.0.0.1:54324
       JWT secret: super-secret-jwt-token-with-at-least-32-characters-long
         anon key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0
 service_role key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU
    S3 Access Key: 625729a08b95bf1b7ff351a663f3a23c
    S3 Secret Key: 850181e4652dd023b7a98c58ae0d2d34bd487ee0cc3254aed6eda37307425907
        S3 Region: local
</code></pre>
<blockquote>
<p>You will want to save them in your notes or create a file called <code>supabase-local-credentials.txt</code>, save these credentials in it and add it to your <code>.gitignore</code>.</p>
</blockquote>
</li>
<li><p>Update Supabase environment variables in your <code>.env.local</code> using the credentials that were generated by Supabase CLI, like below:</p>
<pre><code class="lang-plaintext"> NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
 NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0
 # ... other environement variables
</code></pre>
</li>
<li><p>Use the following command to stop the Supabase setup when done using it.</p>
<pre><code class="lang-bash"> supabase stop
</code></pre>
<blockquote>
<p><strong>Always run this command after you're done with your work session!</strong></p>
</blockquote>
</li>
</ol>
<p>And thats it! Thats how you setup Supabase CLI and use it in your Next.js project.</p>
]]></description><link>https://mywebnotes.xyz/blog/using-supabase-cli-how-to-configure-local-supabase-for-nextjs-development</link><guid isPermaLink="true">https://mywebnotes.xyz/blog/using-supabase-cli-how-to-configure-local-supabase-for-nextjs-development</guid><category><![CDATA[supabase]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[Developer]]></category><category><![CDATA[cli]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Charan Manikanta Nalla]]></dc:creator></item><item><title><![CDATA[How to rate limit your Next.js APIs using Upstash]]></title><description><![CDATA[<h2 id="heading-why-rate-limit-your-apis">Why Rate Limit your APIs?</h2>
<p>Rate limiting APIs is important to prevent abuse or unexpected usage on your APIs.</p>
<p>For example, say you have a public waitlist form to collect emails of your potential users. Here, your API that handles your waitlist form submissions is probably unprotected because you want anyone to be able to sign up in your form without any restrictions or need for authentication.</p>
<p>But, there is chance that your form can be spammed with random/unwanted emails with the help of bots to exhaust your server limits or mess up your database with random emails.</p>
<p>If your API is rate-limited to, say, 5 emails per IP address per 10 minutes, all other submission from that IP address (the user or the bot) are rejected, hence saving you from what is possibly a brute-force attack.</p>
<h2 id="heading-heres-how-to-rate-limit-a-nextjs-api-using-upstash">Heres how to rate limit a Next.js API using Upstash</h2>
<ol>
<li><p>Setup Upstash database on the <a target="_blank" href="https://console.upstash.com/">Upstash console</a>. Once youve signed up or logged in, you should see this screen:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752250124841/cb3a4223-7d29-4056-a809-bfbef59d6ac5.png" alt="Screenshot of a web page titled &quot;Redis&quot; with options for creating a Redis database. It shows sections for commands, average storage, and cost, all displaying zero. A green button labeled &quot;Create database&quot; is in the center. The page header includes navigation options and user profile icons." class="image--center mx-auto" /></p>
</li>
<li><p>Click on Create database and fill in the details like below, choose the free plan for now, and create your database:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752250199168/95c1ccf4-33c5-45cc-8dd7-9a68af735cc3.png" alt="A screenshot of a Redis setup interface displaying a form to create a database. Fields include Name, Primary Region, and optional Read Regions. The region selected is &quot;N. California, USA.&quot; There's a toggle for &quot;Eviction&quot; and a notice to add a payment method for paid plans. Buttons for &quot;Cancel&quot; and &quot;Next&quot; are at the bottom." class="image--center mx-auto" /></p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752250295348/450b82d9-55e1-460e-a22b-1840c2f9befd.png" alt="Screenshot of a Redis database creation page showing the selection of a free plan for &quot;N. California, USA,&quot; with features like Persistence, REST API, TLS, and Global. There's a note to add a payment method for paid plans and buttons to go back or create." class="image--center mx-auto" /></p>
</li>
<li><p>You shouldve been redirected to your databases screen (its a Redis database, by the way).</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752250587898/ebac37dd-ab9b-4442-a832-33bd34e2cfbe.png" alt="Screenshot of a Redis management dashboard showing an endpoint, commands, bandwidth, storage, and cost details. It includes options for connecting using various programming languages like JavaScript and Python." class="image--center mx-auto" /></p>
</li>
<li><p>Hover over your endpoint to see the following copy buttons:</p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752250705178/eabacb48-9c1d-47af-bed5-2ea3d0b65a0b.png" alt="Screenshot of a Redis database interface on Upstash. It shows details like the number of commands, bandwidth, storage, cost, and endpoint information. The connect options include different programming languages such as JavaScript, Python, and PHP." class="image--center mx-auto" /></p>
</li>
<li><p>Put the following environment variables in the <code>.env.local</code>:</p>
<ul>
<li><p>Copy the HTTPS buttons content into the <code>UPSTASH_REDIS_REST_URL</code> environment variable.</p>
</li>
<li><p>Copy the TOKEN buttons content into the <code>UPSTASH_REDIS_REST_TOKEN</code> environment variable.</p>
</li>
<li><p>Your environment variables should include the following (with the real values):</p>
</li>
</ul>
</li>
</ol>
<pre><code class="lang-bash">    UPSTASH_REDIS_REST_URL=xxxxxxxxx
    UPSTASH_REDIS_REST_TOKEN=xxxxxxxxx
</code></pre>
<ol start="6">
<li><p>Install Upstash dependencies: <code>npm install @upstash/ratelimit @upstash/redis</code> in your Next.js project.</p>
</li>
<li><p>Add the following to <code>lib/ratelimit.ts</code></p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">import</span> { Ratelimit } <span class="hljs-keyword">from</span> <span class="hljs-string">'@upstash/ratelimit'</span>;
 <span class="hljs-keyword">import</span> { Redis } <span class="hljs-keyword">from</span> <span class="hljs-string">'@upstash/redis'</span>;

 <span class="hljs-keyword">type</span> Unit = <span class="hljs-string">'ms'</span> | <span class="hljs-string">'s'</span> | <span class="hljs-string">'m'</span> | <span class="hljs-string">'h'</span> | <span class="hljs-string">'d'</span>;
 <span class="hljs-keyword">type</span> Duration = <span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-built_in">number</span>}</span> <span class="hljs-subst">${Unit}</span>`</span> | <span class="hljs-string">`<span class="hljs-subst">${<span class="hljs-built_in">number</span>}</span><span class="hljs-subst">${Unit}</span>`</span>;

 <span class="hljs-comment">// A function to create a ratelimiter instance with a given configuration</span>
 <span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createRateLimiter</span>(<span class="hljs-params">requests: <span class="hljs-built_in">number</span>, duration: Duration</span>) </span>{
   <span class="hljs-comment">// During development, we don't want to rate-limit.</span>
   <span class="hljs-keyword">if</span> (process.env.NODE_ENV === <span class="hljs-string">'development'</span>) {
     <span class="hljs-keyword">return</span> {
       limit: <span class="hljs-function">() =&gt;</span> {
         <span class="hljs-keyword">return</span> {
           success: <span class="hljs-literal">true</span>,
           pending: <span class="hljs-built_in">Promise</span>.resolve(),
           limit: requests,
           remaining: requests,
           reset: <span class="hljs-built_in">Date</span>.now() + <span class="hljs-number">1000</span>,
         };
       },
     };
   }

   <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Ratelimit({
     redis: Redis.fromEnv(),
     limiter: Ratelimit.slidingWindow(requests, duration),
     analytics: <span class="hljs-literal">true</span>,
     <span class="hljs-comment">// Create a unique prefix for each ratelimiter to avoid collisions</span>
     prefix: <span class="hljs-string">`@clndr/ratelimit/<span class="hljs-subst">${requests}</span>-requests/<span class="hljs-subst">${duration.replace(<span class="hljs-string">' '</span>, <span class="hljs-string">''</span>)}</span>`</span>,
   });
 }
</code></pre>
</li>
<li><p>Add this to the Next.js API that you want to rate limit (<code>route.ts</code>)</p>
</li>
</ol>
<pre><code class="lang-typescript">    <span class="hljs-comment">// Your imports</span>
    <span class="hljs-comment">// ...</span>
    <span class="hljs-keyword">import</span> { createRateLimiter } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/lib/ratelimit'</span>;

    <span class="hljs-keyword">const</span> ratelimit = createRateLimiter(<span class="hljs-number">5</span>, <span class="hljs-string">'600 s'</span>);

    <span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">POST</span>(<span class="hljs-params">request: NextRequest</span>) </span>{
      <span class="hljs-keyword">const</span> ip = (request.headers.get(<span class="hljs-string">'x-forwarded-for'</span>) ?? <span class="hljs-string">'127.0.0.1'</span>).split(<span class="hljs-string">','</span>)[<span class="hljs-number">0</span>];
      <span class="hljs-keyword">const</span> { success, limit, remaining, reset } = <span class="hljs-keyword">await</span> ratelimit.limit(ip);

      <span class="hljs-keyword">if</span> (!success) {
        <span class="hljs-keyword">return</span> NextResponse.json(
          { error: <span class="hljs-string">'Too many requests. Please try again later.'</span> },
          {
            status: <span class="hljs-number">429</span>,
            headers: {
              <span class="hljs-string">'X-RateLimit-Limit'</span>: limit.toString(),
              <span class="hljs-string">'X-RateLimit-Remaining'</span>: remaining.toString(),
              <span class="hljs-string">'X-RateLimit-Reset'</span>: reset.toString(),
            },
          },
        );
      }
      <span class="hljs-comment">// Your API code goes here</span>
      <span class="hljs-comment">// ...</span>
     }
</code></pre>
<ol start="9">
<li>Test, Deploy, and Launch!</li>
</ol>
<h2 id="heading-heres-how-to-use-this-with-ai-tools-like-cursor">Heres how to use this with AI tool(s) like Cursor</h2>
<p>After you create the Upstash database and add the environment variables to your <code>.env.local</code> Write a prompt saying:</p>
<blockquote>
<p><em>Implement a rate-limiting for my {your API name} API using the instructions and the code, as is, in this blog post. Ive already setup the Upstash database and added the necessary environment variables to .env.local. Also, do verify if the rate-limiting setup already exists in the codebase. Only add the rate-limiting function code if it doesnt already exist:</em></p>
</blockquote>
<p>And, paste this posts link in an code editor like Cursor and let it handle the coding for you!</p>
<h2 id="heading-ps">P.S.</h2>
<p>And thats it! Hope this helps!</p>
<p>Do you need a website or an app for your business?<br />You can reach out to me at <a target="_blank" href="https://x.com/CharanMNX/">@CharanMNX</a> on X/Twitter or email me at <a target="_blank" href="mailto:charan@devsforfun.com">charan@devsforfun.com</a></p>
<p>Here are my other socials if you wanna talk:</p>
<ul>
<li><p>Instagram: <a target="_blank" href="https://iam.charan.dev">iam.charan.dev</a></p>
</li>
<li><p>X/Twitter: <a target="_blank" href="https://x.com/CharanMNX/">@CharanMNX</a></p>
</li>
<li><p>LinkedIn: <a target="_blank" href="https://www.linkedin.com/in/charan-manikanta/">Charan Manikanta Nalla</a></p>
</li>
<li><p>GitHub: <a target="_blank" href="https://github.com/CharanMN7">CharanMN7</a></p>
</li>
<li><p>YouTube: <a target="_blank" href="https://www.youtube.com/@charan-dev">Charan</a></p>
</li>
<li><p>Website: <a target="_blank" href="https://charan.dev">charan.dev</a></p>
</li>
</ul>
<p>Happy Coding or Vibe Coding!</p>
]]></description><link>https://mywebnotes.xyz/blog/how-to-rate-limit-your-nextjs-apis-using-upstash</link><guid isPermaLink="true">https://mywebnotes.xyz/blog/how-to-rate-limit-your-nextjs-apis-using-upstash</guid><category><![CDATA[Next.js]]></category><category><![CDATA[Upstash]]></category><category><![CDATA[rate-limiting]]></category><category><![CDATA[cursor IDE]]></category><category><![CDATA[cursor ai]]></category><dc:creator><![CDATA[Charan Manikanta Nalla]]></dc:creator></item><item><title><![CDATA[How to create a simple waitlist form in Next.js using Supabase to collect responses]]></title><description><![CDATA[<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Initialize a Next.js project (Next.js 15 recommended) with <strong>Tailwind CSS</strong>.</p>
</li>
<li><p>(Optional) This guide uses <strong>Shadcn UI</strong> components. Install it from the official docs website: <a target="_blank" href="https://ui.shadcn.com/docs">ui.shadcn.com</a></p>
</li>
<li><p>Setup Supabase credentials in <code>.env.local</code></p>
</li>
<li><p>Setup supabase clients and middleware (optional)</p>
</li>
</ul>
<blockquote>
<p><strong>Note:</strong> Replace the <code>&lt;Input /&gt;</code>, <code>&lt;Button&gt;</code>, and <code>&lt;Toast /&gt;</code> components with your own components or default tags if you dont want to install Shadcn UI</p>
</blockquote>
<h2 id="heading-heres-the-notes-on-how-to-create-a-simple-waitlist-form-in-nextjs-collect-responses-from-it-and-store-it-on-supabase">Heres the notes on how to create a simple waitlist form in Next.js, collect responses from it, and store it on Supabase.</h2>
<ol>
<li><p>Create a table called waitlist in the Supabase SQL Editor:</p>
<pre><code class="lang-pgsql"> <span class="hljs-comment">-- 1. Create the table for the waitlist</span>
 <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-built_in">public</span>.waitlist (
   id <span class="hljs-type">bigint</span> <span class="hljs-keyword">GENERATED</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">AS</span> <span class="hljs-keyword">IDENTITY</span> <span class="hljs-keyword">PRIMARY KEY</span>,
   email <span class="hljs-type">text</span> <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-keyword">UNIQUE</span>,
   created_at <span class="hljs-type">timestamptz</span> <span class="hljs-keyword">DEFAULT</span> now()
 );

 <span class="hljs-comment">-- 2. Enable Row Level Security (RLS) on the table</span>
 <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-built_in">public</span>.waitlist <span class="hljs-keyword">ENABLE</span> <span class="hljs-keyword">ROW</span> <span class="hljs-keyword">LEVEL</span> <span class="hljs-keyword">SECURITY</span>;

 <span class="hljs-comment">-- 3. Create a policy that allows public insertion into the table</span>
 <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">POLICY</span> "Allow public insert" <span class="hljs-keyword">ON</span> <span class="hljs-built_in">public</span>.waitlist <span class="hljs-keyword">FOR</span> <span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">WITH</span> <span class="hljs-keyword">CHECK</span> (<span class="hljs-keyword">true</span>);
</code></pre>
</li>
<li><p>Create the API for handling the form submission, <code>/api/waitlist</code>:</p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">import</span> { createClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/utils/supabase/server'</span>;
 <span class="hljs-keyword">import</span> { NextRequest, NextResponse } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/server'</span>;

 <span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">POST</span>(<span class="hljs-params">request: NextRequest</span>) </span>{
   <span class="hljs-keyword">const</span> { email } = <span class="hljs-keyword">await</span> request.json();

   <span class="hljs-keyword">if</span> (!email) {
     <span class="hljs-keyword">return</span> NextResponse.json({ error: <span class="hljs-string">'Email is required'</span> }, { status: <span class="hljs-number">400</span> });
   }

   <span class="hljs-keyword">const</span> supabase = <span class="hljs-keyword">await</span> createClient();

   <span class="hljs-keyword">const</span> { error } = <span class="hljs-keyword">await</span> supabase.from(<span class="hljs-string">'waitlist'</span>).insert([{ email: email.trim().toLowerCase() }]);

   <span class="hljs-keyword">if</span> (error) {
     <span class="hljs-keyword">if</span> (error.code === <span class="hljs-string">'23505'</span>) {
       <span class="hljs-comment">// unique_violation</span>
       <span class="hljs-keyword">return</span> NextResponse.json({ message: <span class="hljs-string">'You are already on the waitlist.'</span> }, { status: <span class="hljs-number">200</span> });
     }
     <span class="hljs-keyword">return</span> NextResponse.json({ error: error.message || <span class="hljs-string">'Something went wrong'</span> }, { status: <span class="hljs-number">500</span> });
   }

   <span class="hljs-keyword">return</span> NextResponse.json({ message: <span class="hljs-string">'You have been added to the waitlist!'</span>, email: email.trim().toLowerCase() });
 }
</code></pre>
</li>
<li><p>Add this waitlist form code to your landing page or wherever you want it:</p>
<pre><code class="lang-typescript"> <span class="hljs-string">"use client"</span>;

 <span class="hljs-keyword">import</span> { Button } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/ui/button"</span>;
 <span class="hljs-keyword">import</span> { Input } <span class="hljs-keyword">from</span> <span class="hljs-string">"@/components/ui/input"</span>;
 <span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
 <span class="hljs-keyword">import</span> { toast } <span class="hljs-keyword">from</span> <span class="hljs-string">"sonner"</span>;
 <span class="hljs-keyword">import</span> { Loader2, Check } <span class="hljs-keyword">from</span> <span class="hljs-string">"lucide-react"</span>;

 <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Page</span>(<span class="hljs-params"></span>) </span>{
   <span class="hljs-keyword">const</span> [email, setEmail] = useState(<span class="hljs-string">''</span>);
   <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">false</span>);
   <span class="hljs-keyword">const</span> [isSuccess, setIsSuccess] = useState(<span class="hljs-literal">false</span>);

   <span class="hljs-comment">// Function to handle the submission</span>
   <span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-keyword">async</span> (e: React.FormEvent&lt;HTMLFormElement&gt;) =&gt; {
     e.preventDefault();
     <span class="hljs-keyword">if</span> (!email) {
       toast.error(<span class="hljs-string">"Please enter your email."</span>);
       <span class="hljs-keyword">return</span>;
     }
     setLoading(<span class="hljs-literal">true</span>);

     <span class="hljs-keyword">try</span> {
       <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'/api/waitlist'</span>, {
         method: <span class="hljs-string">'POST'</span>,
         headers: {
           <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
         },
         body: <span class="hljs-built_in">JSON</span>.stringify({ email: email.trim().toLowerCase() }),
       });

       <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json();

       <span class="hljs-keyword">if</span> (response.ok) {
         toast.success(data.message);
         setEmail(<span class="hljs-string">''</span>);
         setIsSuccess(<span class="hljs-literal">true</span>);
       } <span class="hljs-keyword">else</span> {
         toast.error(data.error || <span class="hljs-string">"Something went wrong."</span>);
       }
     } <span class="hljs-keyword">catch</span> {
       toast.error(<span class="hljs-string">"An unexpected error occurred."</span>);
     } <span class="hljs-keyword">finally</span> {
       setLoading(<span class="hljs-literal">false</span>);
     }
   };

   <span class="hljs-keyword">return</span> (
     &lt;&gt;
       {<span class="hljs-comment">/* Your JSX Code */</span>}
       {<span class="hljs-comment">/* Waitlist form Code: */</span>}
       {isSuccess ? (
         &lt;Button disabled className=<span class="hljs-string">"cursor-default bg-black text-white border disabled:opacity-100"</span>&gt;
           &lt;Check className=<span class="hljs-string">"h-4 w-4 text-green-500"</span> /&gt; you&amp;apos;re on the list!
         &lt;/Button&gt;
       ) : (
         &lt;form onSubmit={handleSubmit} className=<span class="hljs-string">"flex gap-2"</span>&gt;
           &lt;Input
             <span class="hljs-keyword">type</span>=<span class="hljs-string">"email"</span>
             placeholder=<span class="hljs-string">"e.g., naruto@gmail.com"</span>
             value={email}
             onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> setEmail(e.target.value)}
             disabled={loading}
           /&gt;
           &lt;Button <span class="hljs-keyword">type</span>=<span class="hljs-string">"submit"</span> className=<span class="hljs-string">"cursor-pointer"</span> disabled={loading}&gt;
             {loading ? &lt;&gt;&lt;Loader2 className=<span class="hljs-string">"animate-spin mr-2"</span> /&gt; joining...&lt;/&gt; : <span class="hljs-string">'join waitlist'</span>}
           &lt;/Button&gt;
         &lt;/form&gt;
       )}
     &lt;/&gt;
   );
 }
</code></pre>
<blockquote>
<p><strong>Note:</strong> There is some <code>toast</code> based logic in the UI component. Replace the toast logic with <code>console.log()</code> statements if youre not using or havent installed the <code>sonner</code> package.</p>
</blockquote>
</li>
<li><p>Test, Deploy, and Launch!</p>
</li>
</ol>
<h2 id="heading-result">Result</h2>
<p>Here is an example site, called <a target="_blank" href="http://nvyt.xyz">nvyt.xyz</a> that is using the above code:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752249143221/d319ad22-cb2a-4946-ac2e-6e92e2101317.png" alt class="image--center mx-auto" /></p>
<p>Heres the result upon submitting the form:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752249165085/2ea42406-4f44-41cb-803f-541116001768.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p><strong>Note:</strong></p>
<ul>
<li><p>If youre using Shadcn UI components, you should see a similar result in the UI.</p>
</li>
<li><p>Ive used the <code>&lt;Toast /&gt;</code> component in my root layout is how I got that toast, in the bottom right corner.</p>
</li>
</ul>
</blockquote>
<h2 id="heading-heres-how-to-use-this-with-ai-tools-like-cursor">Heres how to use this with AI tool(s) like Cursor</h2>
<p>Write a prompt saying:</p>
<blockquote>
<p><em>Build a waitlist page using the instructions and the code, as is, in this blog post:</em></p>
</blockquote>
<p>And, paste this posts link in an code editor like Cursor and let it handle the coding for you!</p>
<h2 id="heading-bonus">Bonus</h2>
<p>You can refer to my other article about rate-limiting your Next.js APIs like the one we have here, to prevent brute-force attacks bombing your public waitlist form with random email address. It has the AI prompt too, if you just want to skip coding.</p>
<p>Heres the link: <a target="_blank" href="http://mywebnotes.xyz/blog/how-to-rate-limit-your-nextjs-apis-using-upstash">How to rate limit your Next.js APIs using Upstash?</a></p>
<h2 id="heading-ps">P.S.</h2>
<p>And thats it! Hope this helps!</p>
<p>Do you need a website or an app for your business?<br />You can reach out to me at <a target="_blank" href="https://x.com/CharanMNX/">@CharanMNX</a> on X/Twitter or email me at <a target="_blank" href="mailto:charan@devsforfun.com">charan@devsforfun.com</a></p>
<p>Here are my other socials if you wanna talk:</p>
<ul>
<li><p>Instagram: <a target="_blank" href="https://iam.charan.dev">iam.charan.dev</a></p>
</li>
<li><p>X/Twitter: <a target="_blank" href="https://x.com/CharanMNX/">@CharanMNX</a></p>
</li>
<li><p>LinkedIn: <a target="_blank" href="https://www.linkedin.com/in/charan-manikanta/">Charan Manikanta Nalla</a></p>
</li>
<li><p>GitHub: <a target="_blank" href="https://github.com/CharanMN7">CharanMN7</a></p>
</li>
<li><p>YouTube: <a target="_blank" href="https://www.youtube.com/@charan-dev">Charan</a></p>
</li>
<li><p>Website: <a target="_blank" href="https://charan.dev">charan.dev</a></p>
</li>
</ul>
<p>Happy Coding or Vibe Coding!</p>
]]></description><link>https://mywebnotes.xyz/blog/how-to-create-a-simple-waitlist-form-in-nextjs-using-supabase-to-collect-responses</link><guid isPermaLink="true">https://mywebnotes.xyz/blog/how-to-create-a-simple-waitlist-form-in-nextjs-using-supabase-to-collect-responses</guid><category><![CDATA[Next.js]]></category><category><![CDATA[supabase]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Full Stack Development]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Charan Manikanta Nalla]]></dc:creator></item><item><title><![CDATA[Welcome to My Collection of Web Notes]]></title><description><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Hi! Im Charan. Im a software engineer and a freelance full-stack developer. Welcome to my web notes, where I write short (or sometimes long) blog posts about things I learn as I build various SaaS applications. I usually build my apps on the web (but not limited to it), and I constantly learn new things with every new app that I build. And some of these pieces that I write, I see myself rewriting them across projects. Usually, Id put these pieces somewhere like a notes app, but I just never felt like using a notes app for recording these. Referring to GitHub all the time? Not my cup of tea.</p>
<p>So, I decided to get a domain and record all of my notes in a blog under this new domain. Why? I honestly dont know. I just thought itd be easier to search up my notes if I had them here on the web. And this might as well help other people (and LLMs!) to write good code, fast.</p>
<h2 id="heading-my-tech-stack-right-now">My tech stack right now</h2>
<p>Here are my most frequently used technologies for building things:</p>
<ul>
<li><p><strong>Languages:</strong> TypeScript, JavaScript, Python</p>
</li>
<li><p><strong>Libraries:</strong> React, Tailwind CSS, Shadcn UI Components (not a library, I know)</p>
</li>
<li><p><strong>Framework(s):</strong> Next.js</p>
</li>
<li><p><strong>Tools:</strong> Supabase, Upstash Redis, Vercel, Cursor (Code Editor), Google AI Studio</p>
</li>
</ul>
<p>Note that Im limited to these. I know and use a bunch of other tools and technologies in my work, but the above list is what I use the most.</p>
<h2 id="heading-so-why-should-i-care">So, Why should I care?</h2>
<p>Here are a few example topics that Ill be writing about in this blog:</p>
<ul>
<li><p>How to create a simple waitlist form?</p>
</li>
<li><p>How to set up rate limiting for APIs in Next.js using Upstash?</p>
</li>
<li><p>Typical cyberattacks on forms on the web.</p>
</li>
<li><p>How to prompt Cursor like a pro full-stack developer?</p>
</li>
</ul>
<p>and so on.</p>
<p>So, if any of these sound interesting and/or you want human-verified guides to building SaaS appliactions, this blog is for you.</p>
<p>Oh, and subscribe to my newsletter if you wanna get updates whenever I post something! Youll find the signup part at the end of this post.</p>
<h2 id="heading-bonus">Bonus</h2>
<p>If you read this far, heres a tip. If youre building with the same or a similar tech stack as mine, you can actually add any of my blog posts on this website in Cursor under Docs or copy and paste it into the Cursor chat window and ask it to follow the exact same pattern and build that feature or integration in your app. That will ensure that Cursor surely did a good job at writing it, and you dont have to worry about running into an endless loop of error fixing in most cases (hopefully no I see the issue now!s).</p>
<h2 id="heading-faq">FAQ</h2>
<ol>
<li><p><strong>Why?</strong><br /> Why not?</p>
</li>
<li><p><strong>Dont you think its an overkill?</strong></p>
<p> Probably. But Im doing it anyways.</p>
</li>
<li><p><strong>What do you hope to achieve out of this?</strong></p>
<p> Nothing really. Just doing this for fun!</p>
</li>
</ol>
<h2 id="heading-ps">P.S.</h2>
<p>And thats it!</p>
<p>Do you need a website or an app for your business?<br />You can reach out to me at <a target="_blank" href="https://x.com/CharanMNX/">@CharanMNX</a> on X/Twitter or email me at <a target="_blank" href="mailto:charan@devsforfun.com">charan@devsforfun.com</a></p>
<p>Here are my other socials if you wanna talk:</p>
<ul>
<li><p>Instagram: <a target="_blank" href="https://iam.charan.dev">iam.charan.dev</a></p>
</li>
<li><p>X/Twitter: <a target="_blank" href="https://x.com/CharanMNX/">@CharanMNX</a></p>
</li>
<li><p>LinkedIn: <a target="_blank" href="https://www.linkedin.com/in/charan-manikanta/">Charan Manikanta Nalla</a></p>
</li>
<li><p>GitHub: <a target="_blank" href="https://github.com/CharanMN7">CharanMN7</a></p>
</li>
<li><p>YouTube: <a target="_blank" href="https://www.youtube.com/@charan-dev">Charan</a></p>
</li>
<li><p>Website: <a target="_blank" href="https://charan.dev">charan.dev</a></p>
</li>
</ul>
<p>Happy Coding and Vibe Coding!</p>
]]></description><link>https://mywebnotes.xyz/blog/welcome-to-my-collection-of-web-notes</link><guid isPermaLink="true">https://mywebnotes.xyz/blog/welcome-to-my-collection-of-web-notes</guid><category><![CDATA[Web Development]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[supabase]]></category><category><![CDATA[Vercel]]></category><category><![CDATA[shadcn]]></category><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Upstash]]></category><category><![CDATA[Redis]]></category><category><![CDATA[vibe coding]]></category><dc:creator><![CDATA[Charan Manikanta Nalla]]></dc:creator></item></channel></rss>