<?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: Neeraj Mukta</title>
    <description>The latest articles on DEV Community by Neeraj Mukta (@fullstacknrj).</description>
    <link>https://dev.to/fullstacknrj</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%2F449255%2Fb9224810-63c6-48cf-91fa-6add96b6d679.jpg</url>
      <title>DEV Community: Neeraj Mukta</title>
      <link>https://dev.to/fullstacknrj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fullstacknrj"/>
    <language>en</language>
    <item>
      <title>Copilot Taught Me NextJS 15</title>
      <dc:creator>Neeraj Mukta</dc:creator>
      <pubDate>Thu, 07 Aug 2025 06:24:26 +0000</pubDate>
      <link>https://dev.to/fullstacknrj/copilot-taught-me-nextjs-15-c62</link>
      <guid>https://dev.to/fullstacknrj/copilot-taught-me-nextjs-15-c62</guid>
      <description>&lt;p&gt;How GitHub Copilot Taught Me Next.js 15: From Confusion to Production-Ready Code&lt;br&gt;
When I returned to Next.js after a long break, I was overwhelmed by the new App Router, async APIs, and React 19 features. Copilot (and Claude Sonnet 4.1) became my guide. By providing clear context and expectations, I received code that not only worked but followed modern, production-grade patterns. I learned more from Copilot's suggestions than from hours of reading documentation.&lt;/p&gt;

&lt;p&gt;How Copilot Helps You Learn Modern Patterns&lt;br&gt;
Design Patterns &amp;amp; Strategies: Copilot is trained on a vast corpus of public code, including open-source projects that use industry-standard design patterns and idioms. When you prompt Copilot, it often suggests code that reflects best practices and modern approaches.&lt;br&gt;
Learning by Example: Copilot helps you learn by example, showing you how to structure components, use hooks, and implement features the way experienced developers do.&lt;br&gt;
Copilot and Production-Grade Code&lt;br&gt;
Code Quality: Copilot generates code that is syntactically correct and often idiomatic. It follows the conventions of the frameworks and languages you use. For Next.js 15, it understands the new async APIs, caching changes, and React 19 compatibility, as described in the Next.js 15 release notes.&lt;br&gt;
Modern Patterns: For example, Copilot can suggest the new async usage for cookies, headers, and other APIs, and help you migrate to the latest best practices (like using the codemod CLI for upgrades).&lt;br&gt;
Caveat: While Copilot is powerful, it is not infallible. Always review, test, and refactor its suggestions, especially for security, performance, and edge cases.&lt;br&gt;
Copilot for Next.js 15 and New Syntax&lt;br&gt;
Up-to-date Knowledge: Copilot is regularly updated with new code patterns. For Next.js 15, it can help you adopt breaking changes (like async request APIs, new caching defaults, and React 19 features) much faster than reading docs alone.&lt;br&gt;
Migration Help: Copilot can assist with code migrations, such as updating route handlers, using the new &lt;/p&gt; component, and configuring next.config.ts with type safety.&lt;br&gt;
Why Copilot Writes Production-Grade Code&lt;br&gt;
Trained on Real-World Projects: Copilot's training data includes production repositories, so its suggestions often reflect real-world, scalable solutions.&lt;br&gt;
Context Awareness: In VS Code, Copilot uses the surrounding code and your comments to tailor its output, making it more likely to fit your project's architecture and style.&lt;br&gt;
Prompt Engineering: The more context you provide (file structure, comments, function names), the better the output. This is why Copilot can generate code that feels like it was written by a senior engineer.&lt;br&gt;
Action Steps: How to Get the Best Output from Copilot in VS Code&lt;br&gt;
Write Clear Comments: Describe what you want before you start coding. Copilot uses your comments as context.&lt;br&gt;
Provide Examples: If you want a specific style or pattern, show a small example in your code or comments.&lt;br&gt;
Use Copilot Chat: Ask Copilot to explain code, refactor, or generate tests. Use it as a pair programmer, not just an autocomplete tool.&lt;br&gt;
Review and Refactor: Always review Copilot's suggestions. Refactor for readability, performance, and security.&lt;br&gt;
Stay Updated: Keep your Copilot extension and VS Code up to date to benefit from the latest improvements.&lt;br&gt;
Leverage Docs: Use Copilot in conjunction with official documentation (like Next.js docs) for the best results.&lt;br&gt;
Iterate: If the first suggestion isn't perfect, ask Copilot for alternatives or tweak your prompt.&lt;br&gt;
Proofreading &amp;amp; Feedback (Engineering Manager Perspective)&lt;br&gt;
Strengths:

&lt;p&gt;The article is honest about the learning curve and the value Copilot brings.&lt;br&gt;
It provides actionable steps for readers to get the most out of Copilot.&lt;br&gt;
It references official documentation and release notes, making the claims credible.&lt;br&gt;
Areas for Improvement:&lt;/p&gt;

&lt;p&gt;The introduction could be clearer and more engaging.&lt;br&gt;
Some sentences are run-on or have minor typos (e.g., "I actua;;y learnt").&lt;br&gt;
The article could use more structure (headings, bullet points) for readability.&lt;br&gt;
Add a summary or conclusion to reinforce the main message.&lt;br&gt;
Final Thoughts&lt;br&gt;
GitHub Copilot is more than an autocomplete tool—it's a learning partner and productivity booster. For anyone navigating major framework changes (like Next.js 15), Copilot can accelerate your learning and help you deliver production-ready code faster. Just remember: the best results come from collaboration, not blind trust.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>ai</category>
      <category>githubcopilot</category>
    </item>
    <item>
      <title>Bharat general elections 2024 and the paradox of success</title>
      <dc:creator>Neeraj Mukta</dc:creator>
      <pubDate>Sun, 05 May 2024 13:00:04 +0000</pubDate>
      <link>https://dev.to/fullstacknrj/bharat-general-elections-2024-and-the-paradox-of-success-178e</link>
      <guid>https://dev.to/fullstacknrj/bharat-general-elections-2024-and-the-paradox-of-success-178e</guid>
      <description>&lt;p&gt;I will be 28 years old by the end of this year i.e. 2024. And by no measure, do I consider myself a successful person. Success as we all know is a relative metric. For a poor beggar it is to beg a day enough to survive the day, for a rickshaw driver success is to make enough money to provide for the family, for an aspiring youth it is to get a job, for a corporate employee it is to become the boss, for the boss success is well to become a super boss and so on.  &lt;/p&gt;

&lt;p&gt;Do you see a pattern? Success gets redefined every time we achieve it and it’s like a never-ending path that we embark on from the moment we are born.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;But, what is common between Bharat elections this year and the paradox of&lt;/strong&gt;&lt;/em&gt;  &lt;em&gt;&lt;strong&gt;success?&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before, getting to the elections I would like to talk about the “Paradox of success”. Most of us believe that becoming successful will make us happy, and feel respected and loved by the people around us. To be honest it often does but success also entails ‘ENVY’.  The very feeling that makes childhood friends writing an exam bitter when results are declared, despises the person doing well or working hard in life. Sometimes, you don’t even have to be successful to feel the bitterness and jealousy from such people around you just a little spark, a small step towards that path is enough. I know it exists because I’ve felt it throughout my school days, college days, and corporate journey and you must have too!&lt;/p&gt;

&lt;p&gt;Too much or even too little success can break life-long friendships, and families, jeopardize your career, and make you a lonely person wondering what went wrong. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Trust me if you value or crave certain people in life, it’s better to avoid success or to hide it from them.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As we all hear that Bharat is a developing country, she is working hard to match global standards of living, healthcare, infrastructure, military &amp;amp; defense, manufacturing &amp;amp; services, etc.&lt;/p&gt;

&lt;p&gt;Bharat is a nation aspiring to share the table with developed nations. A patriot (or a naive) person like me would think that nations sitting at the top will lend a hand and help us reach there. But is that the case?  Who will benefit from having another developed nation on the earth? What does Bharat has to offer to the world? These are questions raised when someone talks about a developed and prosperous Bharat. Now, who might ask such questions? &lt;em&gt;&lt;strong&gt;You can take a guess, someone who is envious and afraid of our progress.&lt;/strong&gt;&lt;/em&gt; These people are present both inside and outside the Bharat. &lt;/p&gt;

&lt;p&gt;It saddens me and at the same time makes me chuckle when I see rising hate for my nation. A nation that just wants to elevate its citizens. This hate only gets worse with time. When a 3rd world country filled with poor and snake charmers suddenly comes out and helps other nations during a pandemic. When it decides to take a stand for itself and push back the terrorism. &lt;/p&gt;

&lt;p&gt;The propaganda and false narratives are at their peak against my nation. The attempts to tarnish Bharat’s achievement in the last 10 years are rampant. These attacks against our sovereignty are launched by both internal and external actors. What does it signify? Why a democratic country is constantly targeted by a dictator-run country or a deep state-run country?&lt;/p&gt;

&lt;p&gt;I think the answer is simple, “Paradox of success” i.e. &lt;em&gt;&lt;strong&gt;envy. Envy that Bharat might become better than them.&lt;/strong&gt;&lt;/em&gt; Everyone loved Bharat when we were weak and poor. The moment we started walking the path of progress, that love started to fade, we were still too far so the hatred was hidden but it always existed. As we are getting closer and closer to our goals the fear and the jealousy among the peer nations are also peaking. Ladies and gentlemen, Bharat General Election 2024 is that crucial step that will catapult us to a new trajectory, and of course, some developed nations will not like it. &lt;/p&gt;

&lt;p&gt;I don’t know the answer to this paradox or rather human nature of enviousness but my guess is it is all because of evolution and conditioning that life is a zero-sum game. I’m most certainly sure that it is not.&lt;/p&gt;

&lt;p&gt;The lesson is simple: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;"Every time someone sneers at you, tries to belittle you, just smile and tell yourself, “I’m most certainly on the right path to success &amp;amp; start running on that path.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
    </item>
    <item>
      <title>[React Native Guide 2024] Part 1: Builds and flavors.</title>
      <dc:creator>Neeraj Mukta</dc:creator>
      <pubDate>Tue, 23 Apr 2024 04:10:06 +0000</pubDate>
      <link>https://dev.to/fullstacknrj/react-native-guide-2024-part-1-builds-and-flavors-1571</link>
      <guid>https://dev.to/fullstacknrj/react-native-guide-2024-part-1-builds-and-flavors-1571</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“&lt;em&gt;Before building an actual rocket, one should build a prototype. Before experimenting on the actual rocket, one should experiment on the prototype.&lt;/em&gt;” - not elon musk.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: &lt;code&gt;react-native-config&lt;/code&gt; breaks with latesst  react-native 0.74&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/fullstackNRJ/rn-setup-2024" rel="noopener noreferrer"&gt;Source code&lt;/a&gt; for the impatients.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Almost all of the web apps I have ever worked on had some sort of dev/prod parity setup. The larger teams and codebases even had more build types like local, QA, preprod etc. And it does make  sense to separate out your main build from the one being actively developed. It is also in conjecture with &lt;a href="https://12factor.net/" rel="noopener noreferrer"&gt;12 factor app&lt;/a&gt; philosophy.&lt;/p&gt;

&lt;p&gt;In the mobile apps world, we follow something similar called build variants. If you’re not aware of it please read &lt;a href="https://developer.android.com/build" rel="noopener noreferrer"&gt;this &lt;/a&gt;blog first. It explains &lt;code&gt;buildtypes&lt;/code&gt; and &lt;code&gt;productflavors&lt;/code&gt; in detail. To put it in simple words. We have something called Build types, Product flavors cross product of the two gives us Build variants. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 BuildTypes * ProductFlavors = BuildVariants&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The way it works in react native is also similar, we define buildTypes and productFlavor in &lt;em&gt;app/build.gradle&lt;/em&gt; file. BuildTypes usually represent debug and release mode bundle whereas ProductFlavor is further divided into two more axis.&lt;br&gt;
One axis is our &lt;em&gt;environments&lt;/em&gt; like &lt;strong&gt;local, dev, qa, test, prod,&lt;/strong&gt; etc. and the other one &lt;em&gt;flavorDimensions, which&lt;/em&gt; is optional but useful in apps that have for ex. free and pro versions with almost similar code, or uber like separate apps for the riders and the drivers. You can enable flavors and write conditional code which gets built and compiled based on productflavors and flavorDimensions. &lt;/p&gt;

&lt;p&gt;Now that we’re clear about build types and product flavors let’s set it up in a new RN project. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Create a new RN project using React-native cli.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; npx react-native@0.73.6 init MyApp &lt;span class="nt"&gt;--version&lt;/span&gt; 0.73.6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Install&lt;/strong&gt; &lt;em&gt;&lt;strong&gt;react-native-config&lt;/strong&gt;&lt;/em&gt; &lt;strong&gt;package&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;MyApp &lt;span class="k"&gt;**&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;**&lt;/span&gt;  npm i react-native-config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Define config for the build types, product Flavors in app/build.gradle.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Add the following lines in the same file in the defaultConfig block. This will be applied to all the&lt;/p&gt;

&lt;p&gt;buildVarirants and we can also override these values.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;
 &lt;span class="n"&gt;defaultConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;resValue&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"build_config_package"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"com.myapp"&lt;/span&gt;
        &lt;span class="n"&gt;resValue&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"build_config_package"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"com.myapp"&lt;/span&gt;
        &lt;span class="n"&gt;applicationId&lt;/span&gt; &lt;span class="s2"&gt;"com.mmadmin"&lt;/span&gt;
        &lt;span class="n"&gt;minSdkVersion&lt;/span&gt; &lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;minSdkVersion&lt;/span&gt;
        &lt;span class="n"&gt;targetSdkVersion&lt;/span&gt; &lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;targetSdkVersion&lt;/span&gt;
        &lt;span class="n"&gt;versionCode&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"VERSION_CODE"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;toInteger&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;versionName&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"VERSION_NAME"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;b. You’ll notice buildTypes already exists. You can add/edit the configuration related to the specific bundle for ex. enableHermes option or enable source map. We’ll leave it as it is for now.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;buildTypes&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;signingConfig&lt;/span&gt; &lt;span class="n"&gt;signingConfigs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;debug&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;release&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Caution! In production, you need to generate your own keystore file.&lt;/span&gt;
            &lt;span class="c1"&gt;// see https://reactnative.dev/docs/signed-apk-android.&lt;/span&gt;
            &lt;span class="n"&gt;signingConfig&lt;/span&gt; &lt;span class="n"&gt;signingConfigs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;debug&lt;/span&gt;
            &lt;span class="n"&gt;minifyEnabled&lt;/span&gt; &lt;span class="n"&gt;enableProguardInReleaseBuilds&lt;/span&gt;
            &lt;span class="n"&gt;proguardFiles&lt;/span&gt; &lt;span class="nf"&gt;getDefaultProguardFile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"proguard-android.txt"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="s2"&gt;"proguard-rules.pro"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;c. Let’s now define flavorDimensions and productFlavors. Since, we need only one app we will use &lt;code&gt;default&lt;/code&gt; as flavorDismension. But we need 4 productFlavors local, dev, qa, and prod hence we add four blocks with some values. We’re overriding &lt;code&gt;app_name&lt;/code&gt; and adding applicationIdSuffix and versionNameSuffix for each flavor. This will help us differentiate between apps and generate different buildVariants. Helps with testing on the same device or simulator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;flavorDimensions&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt;
    &lt;span class="n"&gt;productFlavors&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;local&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;resValue&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"app_name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"MyApp Debug"&lt;/span&gt;
            &lt;span class="n"&gt;applicationIdSuffix&lt;/span&gt; &lt;span class="s1"&gt;'.debug'&lt;/span&gt;
            &lt;span class="n"&gt;versionNameSuffix&lt;/span&gt; &lt;span class="s2"&gt;"-DEBUG"&lt;/span&gt;
            &lt;span class="n"&gt;minSdkVersion&lt;/span&gt; &lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;minSdkVersion&lt;/span&gt;
            &lt;span class="n"&gt;targetSdkVersion&lt;/span&gt; &lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;targetSdkVersion&lt;/span&gt;

        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;dev&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;resValue&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"app_name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"MyApp Dev"&lt;/span&gt;
            &lt;span class="n"&gt;applicationIdSuffix&lt;/span&gt; &lt;span class="s1"&gt;'.dev'&lt;/span&gt;
            &lt;span class="n"&gt;versionNameSuffix&lt;/span&gt; &lt;span class="s2"&gt;"-DEV"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;versionBuild&lt;/span&gt;
            &lt;span class="n"&gt;minSdkVersion&lt;/span&gt; &lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;minSdkVersion&lt;/span&gt;
            &lt;span class="n"&gt;applicationId&lt;/span&gt; &lt;span class="s2"&gt;"com.reactnativeproject.stage"&lt;/span&gt;
            &lt;span class="n"&gt;targetSdkVersion&lt;/span&gt; &lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;targetSdkVersion&lt;/span&gt;

        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;qa&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;resValue&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"app_name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"MyApp Qa"&lt;/span&gt;
            &lt;span class="n"&gt;applicationIdSuffix&lt;/span&gt; &lt;span class="s1"&gt;'.qa'&lt;/span&gt;
            &lt;span class="n"&gt;versionNameSuffix&lt;/span&gt; &lt;span class="s2"&gt;"-QA"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;versionBuild&lt;/span&gt;
            &lt;span class="n"&gt;minSdkVersion&lt;/span&gt; &lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;minSdkVersion&lt;/span&gt;
            &lt;span class="n"&gt;applicationId&lt;/span&gt; &lt;span class="s2"&gt;"com.reactnativeproject.stage"&lt;/span&gt;
            &lt;span class="n"&gt;targetSdkVersion&lt;/span&gt; &lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;targetSdkVersion&lt;/span&gt;

        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;prod&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
           &lt;span class="n"&gt;resValue&lt;/span&gt; &lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"app_name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"MyApp"&lt;/span&gt;
            &lt;span class="n"&gt;minSdkVersion&lt;/span&gt; &lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;minSdkVersion&lt;/span&gt;
            &lt;span class="n"&gt;applicationId&lt;/span&gt; &lt;span class="s2"&gt;"com.reactnativeproject.prod"&lt;/span&gt;
            &lt;span class="n"&gt;targetSdkVersion&lt;/span&gt; &lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;targetSdkVersion&lt;/span&gt;

        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4:  Setup environments&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create your environment files like .env.local, .env.dev, .env.prod files and add the keys.&lt;/li&gt;
&lt;li&gt;Update &lt;em&gt;&lt;strong&gt;app/build.gradle&lt;/strong&gt;&lt;/em&gt; file with the following code at the beginning of the file.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;
&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ext&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;envConfigFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="nl"&gt;localdebug:&lt;/span&gt;&lt;span class="s2"&gt;".env.local"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;localrelease&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;".env.local"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;devdebug:&lt;/span&gt; &lt;span class="s2"&gt;".env.dev"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;devrelease:&lt;/span&gt; &lt;span class="s2"&gt;".env.dev"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;qadebug:&lt;/span&gt;&lt;span class="s1"&gt;'.env.qa'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;qarelease:&lt;/span&gt;&lt;span class="s1"&gt;'.env.qa'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;proddebug:&lt;/span&gt;&lt;span class="s1"&gt;'.env.prod'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;prodrelease:&lt;/span&gt;&lt;span class="s1"&gt;'.env.prod'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="nl"&gt;from:&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;':react-native-config'&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;projectDir&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPath&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"/dotenv.gradle"&lt;/span&gt;
&lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;versionBuild&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'BUILD_NUMBER'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"+"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'BUILD_NUMBER'&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how we have different combinations for buildTypes and productFlavors like &lt;em&gt;productFlavorbuildTypes&lt;/em&gt;. So, every productFlavor has two buildTypes debug and release.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important, thing here is to match the buildVaraint name with the correct .env file.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, we can access our variables inside the react components&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;HomeScreen&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;View&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;flex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="nx"&gt;ENVIROMENT&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="nx"&gt;ENVIROMENT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:::&lt;/span&gt; &lt;span class="nx"&gt;APP_ID&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="nx"&gt;APP_ID&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Go to Details&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;onPress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Stack_Details&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/View&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also access them inside the &lt;em&gt;&lt;strong&gt;build.gradle&lt;/strong&gt;&lt;/em&gt; file ex.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;versionCode&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"VERSION_CODE"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;toInteger&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To access env variables inside android .java/.kt files&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt; &lt;span class="no"&gt;URL&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="no"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BuildConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;API_URL&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and inside iOS files&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; //import header
#import "ReactNativeConfig.h"
// then read individual key like:
NSString *apiUrl = [ReactNativeConfig envFor:@"API_URL"];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Final Step: Update Package.json with build commands.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, we just need to update scripts to build and run different build variants of our app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"android:local"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-native run-android  --mode=localDebug"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"android:dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"react-native run-android  --mode=devDebug"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"release"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cd android &amp;amp;&amp;amp; .&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;gradlew assembleProdRelease"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"release-aab"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cd android &amp;amp;&amp;amp; .&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;gradlew bundleProdRelease"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"release-dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cd android &amp;amp;&amp;amp; .&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;gradlew assembleDevRelease"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"release-qa"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cd android &amp;amp;&amp;amp; .&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;gradlew assembleQaRelease"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cleanDeps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rm -rf node_modules &amp;amp;&amp;amp; npm i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eslint ."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react-native start"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jest"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;scripts are modified to run on windows terminal.*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We can run the app locally with &lt;code&gt;npm run android:local&lt;/code&gt;, we can generate &lt;code&gt;.apk&lt;/code&gt; for prod, dev, qa with &lt;code&gt;npm run release&lt;/code&gt;, &lt;code&gt;npm run release-dev&lt;/code&gt; , &lt;code&gt;npm run release-qa&lt;/code&gt; respectively. &lt;/p&gt;

&lt;p&gt;And, we can generate &lt;code&gt;.aab&lt;/code&gt; file for playstore submission with  &lt;code&gt;npm run release-aab&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hey, I’m Neeraj Mukta I expertise in crafting resilient apps tailored for startups.  I’ve been crafting web and mobile apps for 8+ years now. If you would like to chat or need assistance with technical problems, feel free to reach out on &lt;a href="https://twitter.com/devnrj07" rel="noopener noreferrer"&gt;X,&lt;/a&gt; &lt;a href="https://www.linkedin.com/in/devnrj07/" rel="noopener noreferrer"&gt;Linkedin&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Serving React app from S3 bucket using Cloudflare CDN</title>
      <dc:creator>Neeraj Mukta</dc:creator>
      <pubDate>Wed, 20 Sep 2023 15:00:17 +0000</pubDate>
      <link>https://dev.to/fullstacknrj/serving-react-app-from-s3-bucket-using-cloudflare-cdn-4d85</link>
      <guid>https://dev.to/fullstacknrj/serving-react-app-from-s3-bucket-using-cloudflare-cdn-4d85</guid>
      <description>&lt;p&gt;The most common way to deploy any static website today is, through AWS. Specifically, S3 bucket in combination with &lt;strong&gt;Certificate Manager&lt;/strong&gt; and &lt;strong&gt;Cloudfront&lt;/strong&gt; services. The combination of these AWS services works well and it's straightforward to set up. However, this may not be the best and easiest solution to host and serve a static website built with frameworks like React, Svelte, or Angular. The major issues in using this combination are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup is not easy, configuring the bucket, then the certificate for SSL, and then creating Cloudfront distribution.&lt;/li&gt;
&lt;li&gt;It becomes expensive as you have to pay for a bucket and CDN service and if you activate WAF that costs you extra. If you have a high-traffic website the costs can rise exponentially.&lt;/li&gt;
&lt;li&gt;There are at least 3 network hops before it reaches the user. It may not add a significant delay but it's absolutely unnecessary.&lt;/li&gt;
&lt;li&gt;There’s no DDoS mitigation system available by default unless we set it up by using another AWS service like AWS Shield or WAF.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fb564328b-0051-4ea8-a5e7-55201819bfe1" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fb564328b-0051-4ea8-a5e7-55201819bfe1" alt="Serving static website with AWS stack" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article, we will look into an alternate way to serve your website securely with an inbuilt &lt;strong&gt;DDoS protection&lt;/strong&gt; and &lt;strong&gt;Bot mitigation system&lt;/strong&gt;. We will use S3 bucket as our primary storage for static files. We will then leverage Cloudflare infrastructure to proxy all the requests to the S3 bucket. By using Cloudflare we get the following advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy to set up HTTPS with automatic SSL certificate generation and renewal for our website.&lt;/li&gt;
&lt;li&gt;Low latency CDN network with caching enabled across the globe&lt;/li&gt;
&lt;li&gt;Cost-effective when compared to the AWS system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fbe0b6db1-c47f-4100-aae5-80d4f146cba8" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fbe0b6db1-c47f-4100-aae5-80d4f146cba8" alt="An alternate approach to serving S3 bucket content" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;The implementation process can be broken down into three steps.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Deploy the static files to a S3 bucket&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Assuming you already have a bucket endpoint ready, I will not explain this step in much detail. If you don’t have a bucket ready, follow &lt;a href="https://writernrj.hashnode.dev/deploy-react-app-to-aws-s3-bucket-in-under-5-mins" rel="noopener noreferrer"&gt;this&lt;/a&gt; article to deploy your app to the bucket and enable static hosting.&lt;/p&gt;

&lt;p&gt;Once, you have static hosting enabled in the bucket you’ll get an HTTP endpoint which you use to access your web app. Something like this &lt;code&gt;[http://app.nrj.agency.s3-website.ap-south-1.amazonaws.com](http://app.nrj.agency.s3-website.ap-south-1.amazonaws.com/admin/default)&lt;/code&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Use CloudFlare name servers
&lt;/h3&gt;

&lt;p&gt;Now, we need to delegate the authority to manage DNS records to Cloudflare. This step is fairly simple. You just need to replace the name server in your registrar dashboard with Cloudflare’s provided nameservers. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a &lt;strong&gt;CloudFlare account&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click on &lt;strong&gt;Add site&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Copy &lt;strong&gt;name servers&lt;/strong&gt; and paste them into your GoDaddy or whichever DNS provider you use.&lt;/li&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fff4ff1e3-699f-4709-87f8-7a89cfbbb5d1" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fff4ff1e3-699f-4709-87f8-7a89cfbbb5d1" alt="Sample DNS names server page in GoDaddy" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you can see all your DNS records in the CloudFlare dashboard. If any of the records are missing please, add them manually.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F5a909b26-5db9-4c2f-90ea-149787f85c13" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F5a909b26-5db9-4c2f-90ea-149787f85c13" alt="DNS records in Cloudflare dashboard" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Enable the proxy mode
&lt;/h3&gt;

&lt;p&gt;If you already have a record pointing to your web app, you’ll notice it is also present in the DNS record list. I’m assuming the CNAME record value points to the CloudFront URL. You need to replace this with your bucket's endpoint from step 1. There’s a toggle to switch between a normal DNS record and a proxy. Turn it on, the cloud should become orange. This indicates that all the traffic to this subdomain is routed through the CloudFlare network. Remember to remove the HTTP prefix from the static site URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fbdd9864c-7c21-48fd-b1fe-6c7d6c83d0da" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fbdd9864c-7c21-48fd-b1fe-6c7d6c83d0da" alt="Creating a record in Cloudflare DNS record manager" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: You have to manage all your records through cloudflare now. Not all records need to be proxied through CloudFlare. You can add normal records just like you would in any other DNS provider.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Try visiting &lt;code&gt;app.yourdomin.com&lt;/code&gt;, (app.nrj.agency in my case) you should see your React app deployed. Check the URL bar it should have an &lt;code&gt;HTTPS&lt;/code&gt; protocol now. This is possible because Cloudflare encrypts the request between the browser and Cloudflare with its own SSL certificate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Ffca31df5-704c-42e1-a2bc-1da59c45a2c5" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Ffca31df5-704c-42e1-a2bc-1da59c45a2c5" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We were able to set up CDN for our S3 bucket in minimum steps. This method of serving content saves time, effort, and money. It also adds an extra layer of safety against common web attacks with zero configuration.&lt;/p&gt;

&lt;p&gt;This method can be used with any similar hosted website be it on Azure or Google Cloud.&lt;/p&gt;

&lt;p&gt;If you found this article helpful. Drop a like and stay tuned for more neat articles.&lt;/p&gt;

&lt;p&gt;This article was published on Medium, Hashnode, Devto using &lt;a href="https://buzpen.com/" rel="noopener noreferrer"&gt;Buzpen&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Deploy React App to AWS S3 bucket in under 5 mins</title>
      <dc:creator>Neeraj Mukta</dc:creator>
      <pubDate>Tue, 19 Sep 2023 15:50:19 +0000</pubDate>
      <link>https://dev.to/fullstacknrj/deploy-react-app-to-aws-s3-bucket-in-under-5-mins-3mi0</link>
      <guid>https://dev.to/fullstacknrj/deploy-react-app-to-aws-s3-bucket-in-under-5-mins-3mi0</guid>
      <description>&lt;p&gt;AWS is the leading Cloud service provider in the world. They offer a variety of services for deploying web applications at scale. These services offload a lot of work that was once required to take any application to production. Traditionally, companies used to have their own servers in their premises to serve their applications to their users. However, with the introduction of cloud-based services, this model has rapidly changed, and more and more companies have moved their infrastructure to the cloud.&lt;/p&gt;

&lt;p&gt;Today, we will look into one such service from AWS called S3 buckets. S3 is shorthand for Simple storage service. It is a service that lets you store files and objects on the cloud.&lt;/p&gt;

&lt;p&gt;From AWS website,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Amazon Simple Storage Service (Amazon S3) is an object storage service that offers industry-leading scalability, data availability, security, and performance. Customers of all sizes and industries can use Amazon S3 to store and protect any amount of data for a range of use cases, such as data lakes, websites, mobile applications, backup and restore, archive, enterprise applications, IoT devices, and big data analytics. Amazon S3 provides management features so that you can optimize, organize, and configure access to your data to meet your specific business, organizational, and compliance requirements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why should we use S3 bucket for static site hosting?
&lt;/h2&gt;

&lt;p&gt;S3 is quite a popular service which is widely used all over the world. It is cheap, reliable, and straightforward to set up. It also has the option to enable static hosting on the bucket items.&lt;/p&gt;

&lt;p&gt;Hence, S3 is quite popular when it comes to deploying SPAs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before diving into the actual process let’s make sure we have the following things ready:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An AWS accoun&lt;/li&gt;
&lt;li&gt;Static files or the build version or your app. (In most of the react apps it is the build directory)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Steps to deploy your app
&lt;/h2&gt;

&lt;p&gt;Step 1: Search for the S3 bucket in the AWS console. Click on the &lt;code&gt;Create Bucket&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F5b704529-0781-4e22-aa03-7b1d30f7df4f" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F5b704529-0781-4e22-aa03-7b1d30f7df4f" alt="create_s3_bucket.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 2: Configure your bucket. Give a name to your bucket. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 It is important to note that the bucket name should match the sub domain/ domain name on which you would like this app to be served on. In my case, I’ve got a domain &lt;a href="http://nrj.agency/" rel="noopener noreferrer"&gt;&lt;code&gt;nrj.agency&lt;/code&gt;&lt;/a&gt; but I want to serve it on the &lt;code&gt;app&lt;/code&gt; subdomain. So, I have named my bucket &lt;a href="http://app.nrj.agency/" rel="noopener noreferrer"&gt;&lt;code&gt;app.nrj.agency&lt;/code&gt;&lt;/a&gt; .&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We have to make our bucket public so that users across the globe can access the files inside it. Acknowledge the warning. Now, the rest of the settings can be left to default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F89bb9ead-6f3d-4f5a-b997-8ec1b1d69a5c" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F89bb9ead-6f3d-4f5a-b997-8ec1b1d69a5c" alt="block_access_1.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save the changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F658b2465-4b55-4915-a22d-af7fc6a86cf5" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F658b2465-4b55-4915-a22d-af7fc6a86cf5" alt="block_access_2.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, you should be able to see the newly created bucket in the Buckets list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F085da3ee-ba27-47c6-bf2d-ac64de8a4513" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F085da3ee-ba27-47c6-bf2d-ac64de8a4513" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Upload Files.&lt;/strong&gt; We can upload files manually by drag and drop or we can use AWS CLI to sync files to our bucket. We will manually put our build folder or the static files in the bucket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F2a1d5628-8e15-4547-bc4f-a0094caa699e" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F2a1d5628-8e15-4547-bc4f-a0094caa699e" alt="upload_1.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F6fe049f5-f675-48a2-b2ac-cd9e05e939d7" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F6fe049f5-f675-48a2-b2ac-cd9e05e939d7" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Set up a bucket policy.&lt;/strong&gt; This step is crucial and required because it tells AWS that this service will be used only to read the objects from it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2012-10-17&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Statement&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PublicReadGetObject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Effect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Allow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Principal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Action&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;s3:GetObject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Resource&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;arn:aws:s3:::app.nrj.agency/*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5: Enable static site hosting.&lt;/strong&gt; In the permissions tab scroll to the bottom of the page. Click on the edit button to enable the static site hosting. Put index.html in both boxes as our SPA or react app will handle the errors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F5cb92018-602f-428e-b4b1-75086dc266ec" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F5cb92018-602f-428e-b4b1-75086dc266ec" alt="static_hosting_1.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F3298d243-39e3-482d-bac6-1089371d20f1" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F3298d243-39e3-482d-bac6-1089371d20f1" alt="static_hosting_2.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s it you can see a URL generated by the AWS which you can use to access your app. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fd18622e2-d283-4e47-bcca-14d39a52c426" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fd18622e2-d283-4e47-bcca-14d39a52c426" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F60bf9033-9bea-4e20-83dc-4bbf64a381ea" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F60bf9033-9bea-4e20-83dc-4bbf64a381ea" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Notice it has HTTP, not HTTPS protocol. This is because S3 doesn’t support it. We need to set up other services to get an SSL certificate and serve it through CDN via HTTPS protocol.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Check out my other blog which tells you how to do this with CloudFlare CDN.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog, we learned how to host static files using an S3 bucket in under 5 mins. All the steps described here can be followed easily.&lt;/p&gt;

&lt;p&gt;If you found this article, give it a thumbs up and share with your friends.&lt;/p&gt;

&lt;p&gt;This article was published on Medium, Devto, and Hashnode using &lt;a href="https://buzpen.com/" rel="noopener noreferrer"&gt;Buzpen&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Setting up a reverse proxy with CloudFlare workers</title>
      <dc:creator>Neeraj Mukta</dc:creator>
      <pubDate>Fri, 08 Sep 2023 23:21:09 +0000</pubDate>
      <link>https://dev.to/fullstacknrj/setting-up-a-reverse-proxy-with-cloudflare-workers-16ki</link>
      <guid>https://dev.to/fullstacknrj/setting-up-a-reverse-proxy-with-cloudflare-workers-16ki</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;What is a reverse proxy in the first place?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In layman's terms, it is a middleman sitting between two parties to make transactions between them smoother, faster, and more secure. Think of a waiter in a busy restaurant. The job of a waiter is to take orders from you and pass them to the kitchen staff and when your order is ready, serve the food from the kitchen to your table. As you can tell, you really don’t have to do much at a restaurant. All the hard work of communication and serving the food is handled by the waiter, here the waiter is acting as a &lt;strong&gt;middleman&lt;/strong&gt; or &lt;strong&gt;reverse proxy&lt;/strong&gt; between you and the chef.&lt;/p&gt;

&lt;p&gt;If we apply this analogy to the digital world, a reverse proxy is a &lt;strong&gt;server&lt;/strong&gt; between your clients (browser, mobile app etc.) and &lt;strong&gt;web servers&lt;/strong&gt;. It can intercept the requests from the client before it reaches the server. This in turn opens a lot of opportunities for improving web server’s characteristics. Just as the waiter in a restaurant takes orders and serves food. It also takes care of billing and payments, managing multiple customers at the same time, managing crow during rush hours etc. A reverse proxy server can also do similar tasks in the internet world like &lt;strong&gt;balancing traffic&lt;/strong&gt; between multiple servers, mitigating &lt;strong&gt;risky behaviors,&lt;/strong&gt; and &lt;strong&gt;caching&lt;/strong&gt; the responses for the same queries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9c0vlvsroqxcps0omwp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9c0vlvsroqxcps0omwp.png" alt="Image description" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But&lt;/strong&gt;, our goal for using a reverse proxy is different.  We want to perform redirects between our websites. In this case, I’ve three websites &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a&lt;/strong&gt;. &lt;strong&gt;Main landing page&lt;/strong&gt; which I want to point to &lt;code&gt;kanvasai.com&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;b. Medium integration page&lt;/strong&gt; which I want to point to &lt;code&gt;kanvasai.com/medium&lt;/code&gt; followed by my app’s query parameters like &lt;code&gt;_?utm_source=&amp;amp;page=&lt;/code&gt;_&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C. Dev. to integration page&lt;/strong&gt; again kanvasai.com/devto&lt;code&gt;followed by my app’s query parameters like _&lt;/code&gt;?utm_source=&amp;amp;page=`_&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F2499291f-1cce-4a69-87cc-5af389b0d57f" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F2499291f-1cce-4a69-87cc-5af389b0d57f" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the sake of brevity all there websites are hosted on &lt;a href="http://webflow.io/" rel="noopener noreferrer"&gt;webflow.io&lt;/a&gt; and these are records on my GoDaddy DNS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Ffae7add9-93fc-4fe0-915d-c50d20fd8dde" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Ffae7add9-93fc-4fe0-915d-c50d20fd8dde" alt="DNS records, Godaddy.com" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice, that I created three records (main, medium, devto) with CNAME pointing to a non-ssl server of &lt;a href="http://webflow.io/" rel="noopener noreferrer"&gt;webflow&lt;/a&gt; (Usually, we use `proxy-ssl.webflow.com). I'll explain later why.&lt;/p&gt;

&lt;p&gt;This gives me three websites with &lt;strong&gt;three&lt;/strong&gt; &lt;strong&gt;different&lt;/strong&gt; &lt;strong&gt;subdomains&lt;/strong&gt; under one domain. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;em&gt;However, an interesting thing to note here is all the web crawlers and robots treat all these three websites as a different entity hence, this hurts the SEO significantly and results in a ranking drop.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://main.kanvasai.com/" rel="noopener noreferrer"&gt;main.kanvasai.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://medium.kanvasai.com/" rel="noopener noreferrer"&gt;medium.kanvasai.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://devto.kanvasai.com/" rel="noopener noreferrer"&gt;devto.kanvasai.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, how do we fix it such that when any crawler/user tries to access the&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://main.kanvasai.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;main.kanvasai.com&lt;/strong&gt;&lt;/a&gt; gets redirected to &lt;strong&gt;kanvasai.com/&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://medium.kanvasai.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;medium.kanvasai.com&lt;/strong&gt;&lt;/a&gt; gets redirected to &lt;strong&gt;kanvasai.com/medium,&lt;/strong&gt; and&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://devto.kanvasai.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;devto.kanvasai.com&lt;/strong&gt;&lt;/a&gt; gets redirected to &lt;strong&gt;kanvasai.com/devto&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Let’s find out how CloudFlare workers can help&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Workers are just javascript code running in Chrome browser environment. The good thing is that it runs closer to your user's browsers. The bad thing is that you cannot use node packages like in Serverless functions but all modern browsers APIs are supported. Read more about them&lt;/em&gt; &lt;a href="https://developers.cloudflare.com/workers/learning/how-workers-works/" rel="noopener noreferrer"&gt;&lt;em&gt;here&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Disclaimer : This article assumes you know little bit about cloudflare workers and have tried building with it. So, basic stuff will be excluded and maybe hyperlinked to other blogs.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note : If you’re going to apply this technique on production apps, please note that every &lt;code&gt;CNAME&lt;/code&gt; record will be intercepted by worker, hence needs to be handled with correct worker logic for every route.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Prerequisite :
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;working CloudFlare account (free tier also works for this tutorial),&lt;/li&gt;
&lt;li&gt;access to your domain registrar (in this case I’m using Godaddy but the process is the same for every other DNS providers),&lt;/li&gt;
&lt;li&gt;Any code editor that supports JS/TS language features(I will use VS code),&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1 : Create a worker project with Cloudflare cli&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Create a new worker project by running the following command in the terminal. &lt;code&gt;website-stitcher&lt;/code&gt; is the project name&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create cloudflare@2 &lt;span class="nt"&gt;--&lt;/span&gt; website-stitcher
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running this command, CLI will create a directory named &lt;code&gt;web-stitcher&lt;/code&gt; and you’ll be prompted through a series of questions,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;choose &lt;strong&gt;hello world&lt;/strong&gt; worker&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F80c3b5c3-edfd-4ab5-8d3a-4d48a454169a" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F80c3b5c3-edfd-4ab5-8d3a-4d48a454169a" alt="Untitled.png" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;b. Always say &lt;strong&gt;yes&lt;/strong&gt; to typescript&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fd36b2cdb-4d4c-47e4-9b4a-1a88bc92d78d" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fd36b2cdb-4d4c-47e4-9b4a-1a88bc92d78d" alt="Screenshot_2023-08-16_at_8.44.30_PM.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;c. git version control (you can do this later also)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F9ff9b788-3cc1-4abc-8059-1ef71c043ff5" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F9ff9b788-3cc1-4abc-8059-1ef71c043ff5" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;d. Yes, deploy our sample worker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F1a14f08d-85ac-4311-b8eb-d060d692b14e" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F1a14f08d-85ac-4311-b8eb-d060d692b14e" alt="Untitled.png" width="800" height="743"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After selecting ‘yes’, Cloudflare cli will try to log in to your Cloudflare account via browser. Authorize the authentication of cli with the account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F6497e21d-66d0-4892-a23c-9a9459a59d44" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F6497e21d-66d0-4892-a23c-9a9459a59d44" alt="Untitled.png" width="800" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and Voila! we have our Cloudflare worker ready and deployed. (A browser window should open up automatically with the message ‘hello world’)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fe8e5dba7-67ef-49e1-8721-646d452fce82" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fe8e5dba7-67ef-49e1-8721-646d452fce82" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we have a Cloudflare worker boilerplate code ready. Open it in your editor of choice. We don’t need to worry about all the files now. We’ll write all our logic in &lt;code&gt;src/worker.ts&lt;/code&gt; file&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F0f1b65e7-fb07-4153-bb17-8fb1c829e544" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F0f1b65e7-fb07-4153-bb17-8fb1c829e544" alt="Untitled.png" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 2: Write the logic to handle incoming requests and redirect its response&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The fetch function has access to the request object which we will use to identify domains, subdomains, pathname, search params, etc. using the below snippet.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fe37fe5fb-cc71-4964-9353-12003f9477b0" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fe37fe5fb-cc71-4964-9353-12003f9477b0" alt="Untitled.png" width="800" height="81"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s define our constants &lt;strong&gt;ROOT_DOMAIN&lt;/strong&gt;, &lt;strong&gt;SUBDOMAINS&lt;/strong&gt;, &lt;strong&gt;MAIN_SUBDOMAIN&lt;/strong&gt; and main &lt;strong&gt;WEBSITE_URL.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F31efa7cb-7dcc-492f-aa68-f58fb20a9aab" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F31efa7cb-7dcc-492f-aa68-f58fb20a9aab" alt="Untitled.png" width="800" height="103"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we need to write js/ts code to check the incoming hostname for subdomains and redirect it to sub- route. Something like this,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F81016548-f1c7-4850-a7be-45be059f29cc" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F81016548-f1c7-4850-a7be-45be059f29cc" alt="Untitled.png" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By looking at the test cases we can break down the logic into three main blocks&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if the &lt;strong&gt;hostname&lt;/strong&gt; is not the same as the &lt;strong&gt;ROOT_DOMAIN&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;here, we need to check SUBDOMAINS if they match the hostname’s subdomain. If it matches one of the SUBDOMAINS we need to redirect the user to the right url.&lt;/li&gt;
&lt;li&gt;we should also check if it matches our MAIN_SUBDOMAIN and redirect the user to the main website&lt;/li&gt;
&lt;li&gt;If does not meet the above two conditions then just return data from the original request URL.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fui28kemewx2fr8umyje7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fui28kemewx2fr8umyje7.png" alt="Image description" width="800" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if the &lt;strong&gt;pathname&lt;/strong&gt; matches one of the &lt;strong&gt;SUBDOMAINS&lt;/strong&gt; and the &lt;strong&gt;hostname&lt;/strong&gt; is the same as &lt;strong&gt;ROOT_DOMAIN&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;here, we just need to fetch the data from the matching subdomain and redirect the user to the correct URL.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6mi8toi0xo201d2rvoe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6mi8toi0xo201d2rvoe.png" alt="Image description" width="800" height="227"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- or if there’s no subdomain or subroute, fetch data from **MAIN_DOMAIN** and return a response or redirect the user.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fng23cybcgrgvjayq5qgl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fng23cybcgrgvjayq5qgl.png" alt="Image description" width="800" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the pathname has a trailing &lt;code&gt;slash&lt;/code&gt; (important case)

&lt;ul&gt;
&lt;li&gt;SEO crawlers treat &lt;code&gt;kanvasai.com/medium and [&lt;/code&gt;kanvasi.com/medium/&lt;code&gt;](http://kanvasi.com/medium/) as separate routes. Hence, we need to take care of the&lt;/code&gt;trailing slash`. It’s easy with a bunch of js string methods.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkk2u2pyjgjcscq80vtwf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkk2u2pyjgjcscq80vtwf.png" alt="Image description" width="800" height="193"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It took some time and hit and trail to come up with logic and could be a little overwhelming to understand at once. Hence, I’m attaching a flow diagram to visualize the conditions.&lt;/p&gt;

&lt;p&gt;If you prefer you can just copy and paste the same code change values of constants ROOT_DOMAIN, SUBDOMAINS, MAIN_SUBDOMAIN, and main MAIN_URL to your records.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fa496359c-4e84-46c5-b4f5-6c3e9c4c8dba" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fa496359c-4e84-46c5-b4f5-6c3e9c4c8dba" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The complete code looks like this&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/fullstackNRJ/7c8a1a12612749f04dc08c6403fe4789" rel="noopener noreferrer"&gt;embed&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run the following command to deploy the code/worker&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
npx wrangler deploy&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Head over to the Cloudflare &lt;em&gt;&lt;strong&gt;dashboard &amp;gt; workers &amp;amp; pages&lt;/strong&gt;&lt;/em&gt;. You should see list of all the workers here. I have multiple workers but if it’s a new account you should see just one worker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F2ca88147-9621-457d-ae81-c1b0da3562eb" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F2ca88147-9621-457d-ae81-c1b0da3562eb" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigate inside your worker by clicking on it. You can see all relevant information related to your worker. There are tabs like triggers, logs, deployments, etc.&lt;/p&gt;

&lt;p&gt;Next, we need to define the trigger for this worker. Right now, it is deployed to some random URL by Cloudflare.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 3: Register your site with Cloudflare zone&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before adding a trigger for our worker we need to delegate our DNS nameserver to the Cloudflare. (If your DNS registrar is Cloudflare you don’t need to do this step).&lt;/p&gt;

&lt;p&gt;On the main dashboard, click &lt;code&gt;Add Site&lt;/code&gt; button on the top right corner, and enter your domain name ex. kanvasai.com. &lt;/p&gt;

&lt;p&gt;Now, follow the steps shown by Cloudflare for moving nameservers. This will allow Cloudflare to fetch all the existing DNS records. But, please make sure you have all the records present. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fb60e733c-02de-478d-b945-ba4d94b16949" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fb60e733c-02de-478d-b945-ba4d94b16949" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding &lt;a href="http://albert.ns.clouflare.com/" rel="noopener noreferrer"&gt;&lt;code&gt;albert.ns.clouflare.com&lt;/code&gt;&lt;/a&gt; and &lt;a href="http://annabel.ns.cloudflare.com/" rel="noopener noreferrer"&gt;&lt;code&gt;annabel.ns.cloudflare.com&lt;/code&gt;&lt;/a&gt; to my goDaddy domain DNS setting page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Ffa17348b-8653-4ab1-9bfc-a94743d15ae0" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Ffa17348b-8653-4ab1-9bfc-a94743d15ae0" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finish the site setup by clicking continue and check the DNS nameserver status. This may take some time to verify new nameservers. &lt;em&gt;&lt;strong&gt;Important, thing to note here is now you have to manage all your DNS records from your Cloudflare account. Your DNS registrar will not have control over your records.’&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fcc90a152-a517-47c8-ada1-5c81de5b78ca" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fcc90a152-a517-47c8-ada1-5c81de5b78ca" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;/em&gt; &lt;em&gt;: Make sure you have all the records available in the cloudflare DNS records too. In my case, my two records were not transferred so I had to add them again manually. This can cause&lt;/em&gt; &lt;em&gt;&lt;strong&gt;downtime&lt;/strong&gt;&lt;/em&gt; &lt;em&gt;to your app so be careful while doing this transfer.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 4: Add your root domain as the worker’s trigger&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now, we are ready to add triggers to our workers. Navigate to your worker by clicking workers &amp;amp; pages on the left panel and then clicking on your worker from the list.&lt;/p&gt;

&lt;p&gt;On this page, click on the &lt;code&gt;Triggers&lt;/code&gt; tab next to &lt;code&gt;Metrics.&lt;/code&gt;  Here, click on the &lt;strong&gt;Add Custom Domain&lt;/strong&gt; button and add your domain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F4796a68e-0206-40f0-a7f4-1e7a5928d392" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F4796a68e-0206-40f0-a7f4-1e7a5928d392" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: you might see this error if you did not follow the last step correctly. In order to add your custom domain as a trigger for workers, your custom domain must be delegated to Cloudflare nameservers.(Don’t worry, your registrar will stay the same, GoDaddy in this case).&lt;/p&gt;

&lt;p&gt;Cloudflare adds a new DNS record to type worker, you can check this to verify if your custom domain is set up correctly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F020f453f-cfbf-4dfe-bd6b-e15babd98ac4" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F020f453f-cfbf-4dfe-bd6b-e15babd98ac4" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 5: Attach a wildcard route to your worker&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now, for our worker logic to run correctly we need to forward all the requests from all the sub domains to our worker. So, we’ll add a route for this we are going to use a wildcard (&lt;em&gt;) to define a pattern of URLs that are allowed or that trigger our worker. So,&lt;/em&gt; just below the custom domain, there is a section for routes (also called worker routes).  I’ll add &lt;code&gt;*.kanvasai.com/*&lt;/code&gt;  as the route and choose &lt;a href="http://kanvasi.com/" rel="noopener noreferrer"&gt;&lt;code&gt;kanvasi.com&lt;/code&gt;&lt;/a&gt; as the zone. This represents all the subdomains and subroutes that have &lt;a href="http://kanvasi.com/" rel="noopener noreferrer"&gt;kanvasi.com&lt;/a&gt; as the root domain.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F371ded2c-8662-4d60-98bd-24655ae1c1c4" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F371ded2c-8662-4d60-98bd-24655ae1c1c4" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;we should see this message once the route is properly configured.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fe1c0209a-7e20-4772-af08-0b9bb34e1ec4" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fe1c0209a-7e20-4772-af08-0b9bb34e1ec4" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we can already see the worker in action. Try visiting your root domain and subdomains.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F5ec867db-209c-4735-abf6-c0673921f5da" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F5ec867db-209c-4735-abf6-c0673921f5da" alt="ezgif.com-video-to-gif.gif" width="600" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If it's not working for you wait for some time to let DNS propagate changes. Try on a different device because Chrome caches the websites on disk.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 6: Handling www CNAME record :&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;There are a couple of ways to handle this, &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We can add another CNAME record pointing to the same Cloudflare worker,&lt;/li&gt;
&lt;li&gt;handle it in the worker code with JS or,&lt;/li&gt;
&lt;li&gt;iii. Define a page rule such that anyone accessing &lt;a href="http://www.kanvasai.com/*" rel="noopener noreferrer"&gt;&lt;strong&gt;www.kanvasai.com/&lt;/strong&gt;*&lt;/a&gt; gets automatically redirected to &lt;strong&gt;&lt;a href="https://kanvasai.com/" rel="noopener noreferrer"&gt;https://kanvasai.com/&lt;/a&gt;&lt;/strong&gt;*&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fee7d2053-2e64-47b6-8c2a-a08df2060b2b" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fee7d2053-2e64-47b6-8c2a-a08df2060b2b" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 7: Handling relative URL (hrefs) in your applications&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If your app uses an anchor tag and has subroutes, you should probably prefix those with subdomain names (/medium/about or /devto/about). You’ll understand why after trying it.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 8: SSL/TLS certificates between Cloudflare and your application server&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For Vercel and similar applications, SSL is generated by the platform itself. Hence, we need full security&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;Webflow&lt;/strong&gt;, we keep it flexible and let Cloudflare manage SSL certificates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Faf82d30c-bf05-4a7d-a3fa-432f91ab6232" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Faf82d30c-bf05-4a7d-a3fa-432f91ab6232" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cons of this approach&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Now, we’ll have to manage all our DNS records through Cloudflare.&lt;/li&gt;
&lt;li&gt;Adding a new record becomes a little tricky.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;In the follow-up article, we’ll see how to do this with SPAs.&lt;/strong&gt; Please comment if you want a tutorial on that topic.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are trying to setup this reverse proxy and facing any issues, feel free to reach out or book a &lt;strong&gt;free&lt;/strong&gt; consultation &lt;a href="https://calendly.com/niraj-mukta123/introductory-call?month=2023-09" rel="noopener noreferrer"&gt;here&lt;/a&gt;. I’ll be more than happy to help.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article was published using&lt;a href="https://buzpen.com/" rel="noopener noreferrer"&gt; Buzpen.&lt;/a&gt; If you want to publish the same article to three major platforms at once, then do try out Buzpen.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building an Android and iOS app in 2023</title>
      <dc:creator>Neeraj Mukta</dc:creator>
      <pubDate>Wed, 06 Sep 2023 15:10:05 +0000</pubDate>
      <link>https://dev.to/fullstacknrj/building-an-android-and-ios-app-in-2023-4lol</link>
      <guid>https://dev.to/fullstacknrj/building-an-android-and-ios-app-in-2023-4lol</guid>
      <description>&lt;p&gt;“Ideas are cheap, execution is expensive!” &lt;/p&gt;

&lt;p&gt;I’m sure you must have heard this quote in some or the other form. A billion dollar ideas requires millions in investments before it turns profitable. The road to profitability is a tough one as we have seen in the recent few years with companies adapting all the means to save every buck. &lt;/p&gt;

&lt;p&gt;So, the question is should you also spend millions and then start saving thousands to become profitable? I leave this question to be answered by you.&lt;/p&gt;

&lt;p&gt;In the past few months I have met at least 10 founders (or wanna be founders) who have this startup or business idea that they think is amazing, has potential to disrupt the market and in turn  make them rich. Of course, this was all in their heads and they were trying hard to pitch the idea and find a co founder who can help them build the product or service. And one common question they had was how expensive is it to build a product or service? Mind you all these people didn’t have a technical background and the only thing according to them that was stopping them was tech. The wild wild world of app development.&lt;/p&gt;

&lt;p&gt;My goal through this article is to help you make right technological decisions to test your ideas. That’s right! Testing an idea is the first stage of any &lt;strong&gt;billion dollar&lt;/strong&gt; company in making. You need to bet enough resources and time to validate your &lt;em&gt;product idea&lt;/em&gt;, &lt;em&gt;find early adopters&lt;/em&gt; and then do some serious analytics. Most of the founders make a mistake here, they go all in on the product development and marketing without even proper development and business plan. There are many steps involved before committing to full on development. (Maybe I’ll cover those in some other blog.)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;TIP 1: Save money by building something trivial but functional first that can help you validate the idea. It does not have to be an app or a website. It could be a simple spreadsheet also.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most of the time they end up losing money and sometimes reputation too. You certainly don’t have to sell your house to validate your product idea.&lt;/p&gt;

&lt;p&gt;Returning to the original question how much does it cost to build an MVP (minimal viable product)?&lt;/p&gt;

&lt;p&gt;Well, the answer varies on the requirements of your MVP. Let’s assume you need an android or iOS app or both for your MVP or initial version. The effort in building any native mobile app from scratch is significantly high when compared to web apps. It takes around 100 hrs just to write basic building blocks of an native app and time increases based on complexity of business rules.&lt;/p&gt;

&lt;p&gt;You can easily save time and effort by choosing a cross-platform technology like &lt;a href="https://reactnative.dev/" rel="noopener noreferrer"&gt;&lt;strong&gt;React Native&lt;/strong&gt;&lt;/a&gt;. This helps you in jumpstarting the native app development with little knowledge on native languages like Java/Kotlin (used to build native android apps), Objective C/Swift (used to build native iOS apps).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;TIP 2: If you need an app for your idea, choose a cross platform technology to build it. This will help you build it faster, iterate over features quickly and roll out to users in shortest amount of time without burning a hole in your pocket.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Honestly, &lt;a href="https://reactnative.dev/" rel="noopener noreferrer"&gt;&lt;strong&gt;React native&lt;/strong&gt;&lt;/a&gt; has become pretty good in 2023 and &lt;a href="https://expo.dev/about" rel="noopener noreferrer"&gt;&lt;strong&gt;expo team&lt;/strong&gt;&lt;/a&gt; has made it even easier for any javascript developer to build cross platform mobile apps. No matter what stage you are at as a company you should almost always bet on react native because it’s very stable and rapid to develop. If you’re just starting out or you are already at startup level you should save costs on technology by leveraging &lt;a href="https://expo.dev/" rel="noopener noreferrer"&gt;expo&lt;/a&gt; and react native ecosystem. If you google how to build a mobile app for android and iOS you will find tons of frameworks and technology to do it. But, broadly speaking there are two type of app development native and cross-platform, cross-platform being the quickest of the two.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;TIP 3: In 2023, you can use&lt;/strong&gt; &lt;a href="https://reactnative.dev/" rel="noopener noreferrer"&gt;&lt;strong&gt;React native&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;/Expo to build prototype apps as well as high traffic  professional quality apps&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s discuss a development plan where you want an app (android/ios) that users can use to find their morning ride to office. &lt;strong&gt;A social carpooling app&lt;/strong&gt;. Now, before making this into a flow blown app something like the Uber and then launching it to your customers, what you should do first is to figure out if the users actually want to carpool with strangers in their area?  Just ask around, interview people. Once, you are sure then you should test it by giving them an app.&lt;/p&gt;

&lt;p&gt;For, this you can start with simple website that lists all the available cars from region A to region B in that city. Mobile devices are more common for such services so let’s focus on building the iOS and android app. Breaking down the requirements looks something like :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;people signup into your app,&lt;/li&gt;
&lt;li&gt;they share some identity like driver’s license and other relevant information,&lt;/li&gt;
&lt;li&gt;they can view available car pools for today,&lt;/li&gt;
&lt;li&gt;they can send a request to join a certain car pool,&lt;/li&gt;
&lt;li&gt;and they have the an option to host a car themselves and approve new requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I just did is to write down a high level overview of what app should do and this should be enough to validate our idea. As you can already see we need at least 5 screens already for our app. Imagine building these same screens separately on iOS and android &amp;amp; this will also need a backend. 😱 Thus, by using a cross platform framework like React Native or &lt;a href="https://flutter.dev/" rel="noopener noreferrer"&gt;flutter&lt;/a&gt; we can save time and cost on app and maybe spend that time on building a robust server. Because at the end data is what matters in this century.  So I’m picking expo for few simple reasons,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it’s open source &amp;amp; it’s based on Typescript and React,&lt;/li&gt;
&lt;li&gt;it complies down to actual native code for both android and iOS unlike flutter,&lt;/li&gt;
&lt;li&gt;it has relatively less steep learning curve, it’s easy to find devs or train React devs,&lt;/li&gt;
&lt;li&gt;it has good support/libraries for camera, location, bluetooth etc, all the common native functionalities,&lt;/li&gt;
&lt;li&gt;it is extensible meaning you can write custom native plugins if existing components or libraries are not suitable enough.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;em&gt;Also, why not Flutter? will be answered in an separate post.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My next post will be on setting up a React Native app with expo. Stay tuned and follow me on whichever platform you are reading this.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;PS: I provide free consultation on MVP Planning. I do a 1 on 1 session where we figure out the viability of your idea and how to bring it to life in most appropriate and quickest manner possible. If you are struggling with making right technological choice for your Product feel free to reach out. You can book free consultation &lt;a href="https://calendly.com/niraj-mukta123/introductory-call?month=2023-09" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article was publish on &lt;em&gt;medium&lt;/em&gt;, &lt;em&gt;devto&lt;/em&gt; and &lt;em&gt;hasnode&lt;/em&gt; using &lt;a href="https://buzpen.com/" rel="noopener noreferrer"&gt;buzpen&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Add a Likes counter to your website with Cloudflare workers</title>
      <dc:creator>Neeraj Mukta</dc:creator>
      <pubDate>Fri, 01 Sep 2023 09:00:40 +0000</pubDate>
      <link>https://dev.to/fullstacknrj/add-a-likes-counter-to-your-website-with-cloudflare-workers-1fci</link>
      <guid>https://dev.to/fullstacknrj/add-a-likes-counter-to-your-website-with-cloudflare-workers-1fci</guid>
      <description>&lt;p&gt;So you have spent time and money to build a &lt;strong&gt;beautiful-looking website&lt;/strong&gt; for your &lt;strong&gt;product&lt;/strong&gt; or maybe a fancy &lt;strong&gt;portfolio website&lt;/strong&gt; to impress your potential clients cool! You also added &lt;strong&gt;Google Analytics&lt;/strong&gt; to track traffic but you can’t tell how many people liked it based on analytics data. 😟&lt;/p&gt;

&lt;p&gt;Well, what if I show you a simple way to track it? And the answer is simple: just add a &lt;strong&gt;like button&lt;/strong&gt; ♥️. We see those everywhere on every platform today since Facebook introduced it to the world. &lt;/p&gt;

&lt;p&gt;But why serverless solution or Cloudflare worker to be specific? Good question. Yes, we can do it the regular way with an API from a dedicated Nodejs or python web server with &lt;a href="https://www.mongodb.com/" rel="noopener noreferrer"&gt;MongoDB&lt;/a&gt; approach but it will be an overkill to develop &amp;amp; deploy a server with a database just to keep track of like counts. (at least in my case)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our goal here is to:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to store the Likes count at some global storage so that we can read the updated value anytime,&lt;/li&gt;
&lt;li&gt;increase the Likes count when a user clicks our like button and decrease the Likes count when they click it again,&lt;/li&gt;
&lt;li&gt;Learn how to develop and deploy an API in minutes (with &lt;a href="https://developers.cloudflare.com/workers/wrangler/" rel="noopener noreferrer"&gt;Wrangler-cli&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Assumptions / Prerequisite :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An active Cloudflare account&lt;/li&gt;
&lt;li&gt;any code editor&lt;/li&gt;
&lt;li&gt;Node version &amp;gt; 16 installed&lt;/li&gt;
&lt;li&gt;Some knowledge about React and Rest APIs&lt;/li&gt;
&lt;li&gt;Basic javascript/Typescript skills&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before, jumping into the coding part first let’s try to break down the process and define some steps to implement the feature. Let’s start with the API part we need three endpoints for our use case&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;/getTotalLikes&lt;/code&gt;: to get a current count of likes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/incrementLikes&lt;/code&gt;: to increase Likes count by 1 when the user clicks the Like button&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/decrementLikes&lt;/code&gt;: to decrease Likes count by 1 when the user clicks the Like button again (unlike)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once we have these three endpoints, we can hook them up to the react button component and also display the latest Likes count around it.&lt;/p&gt;

&lt;p&gt;We have a small plan now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Let’s begin building:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1&lt;/strong&gt;: Create a &lt;code&gt;hello word&lt;/code&gt; Cloudflare worker project using the below command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create cloudflare@latest

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

&lt;/div&gt;



&lt;p&gt;It will walk you through boilerplate setup. Feel free to pick any configuration you like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My project config&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;Typescript: Always, Yes. &lt;/p&gt;

&lt;p&gt;Deploy: Yes. It will open a live URL in the browser. (easy to deploy)&lt;/p&gt;

&lt;p&gt;Whoami: login to Cloudflare account via browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fa846acd5-6c6b-467a-ad0f-c7811dc6c3d2" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fa846acd5-6c6b-467a-ad0f-c7811dc6c3d2" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Creating a KV Namespace&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;KV stands for key-value storage it works like a browser local storage but it’s not stored on the client’s browser but rather close to the browser in the cdns. Read more about it &lt;a href="https://developers.cloudflare.com/workers/learning/how-kv-works" rel="noopener noreferrer"&gt;here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run the following command :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx wrangler kv:namespace create LIKES
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fdd0e867a-8030-4dd7-aff7-2c7d81fe1ce3" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fdd0e867a-8030-4dd7-aff7-2c7d81fe1ce3" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, copy over values from the terminal to the wrangler.toml file of your project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kv_namespaces &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt; binding &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;YOUR_BINDING&amp;gt;"&lt;/span&gt;, &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;YOUR_ID&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F269b7e6a-da7a-4bd3-96e3-3691088d9e4c" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F269b7e6a-da7a-4bd3-96e3-3691088d9e4c" alt="Screenshot of wrangler.toml file after adding kv namespace binding" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Think of namespaces as different collections of a redis-cache, it can store. up to 10,000 documents with a max size of 25MB for each value. Read, more about the limits of KV Namespaces &lt;a href="https://developers.cloudflare.com/workers/platform/limits/#kv-limits" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, we are ready to use KV namespace inside our worker. The KV Namespace provides two methods, to put and to get the data.&lt;/p&gt;

&lt;p&gt;1.&lt;code&gt;put(key:string, value:string|ReadableStream|ArrayBuffer):Promise&amp;lt;void&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This method returns a &lt;code&gt;Promise&lt;/code&gt; that you should &lt;code&gt;await&lt;/code&gt; on in order to verify a successful update.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;get(key:string):Promise&amp;lt;string|ReadableStream|ArrayBuffer|null&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The method returns a promise you can &lt;code&gt;await&lt;/code&gt; on to get the value. If the key is not found, the promise will resolve with the literal value &lt;code&gt;null&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```bash
export default {
  async fetch(request, env, ctx) {
        await env.NAMESPACE.put("first-key", "1")
    const value = await env.NAMESPACE.get("first-key");

    if (value === null) {
      return new Response("Value not found", { status: 404 });
    }
    return new Response(value);
  },
};
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;or we can use cli command to set the initial key-value pair
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```bash
npx wrangler kv:key put --binding=LIKES "likes_count" "0"
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**In the src/worker.ts file**

1. uncomment line 13 and rename `MY_NAMESPACE` with `LIKES`
2. Inside the fetch function on line 30 add the above code to get value from the store.

As you can see we can leverage typescript to be confident about KV Namespace during runtime 


![worker.ts: accessing ‘LIKE’ KVNamespace inside the worker](https://s3.us-west-2.amazonaws.com/secure.notion-static.com/7974c0a4-ba94-4286-afad-79aa2cd583b5/Untitled.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;amp;X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&amp;amp;X-Amz-Credential=AKIAT73L2G45EIPT3X45%2F20230901%2Fus-west-2%2Fs3%2Faws4_request&amp;amp;X-Amz-Date=20230901T090007Z&amp;amp;X-Amz-Expires=3600&amp;amp;X-Amz-Signature=728b173f5719def159624798310b7201a940124eff1662bd8a9747d60617b55f&amp;amp;X-Amz-SignedHeaders=host&amp;amp;x-id=GetObject)


Let’s deploy this worker by running the following command:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```bash
npx wrangler deploy
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F80e9fe28-3c74-484f-a920-d648a626a2a1" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F80e9fe28-3c74-484f-a920-d648a626a2a1" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open the URL in your browser&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fb93c20de-d517-42b5-8949-d30ce2d53d39" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fb93c20de-d517-42b5-8949-d30ce2d53d39" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I think you must have understood what’s happening here and how we can interact with KV storage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Handle API endpoints&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s circle back to our requirements we need an endpoint &lt;code&gt;/getLikesCount&lt;/code&gt; which will return updated likes_count from the KV store.&lt;/p&gt;

&lt;p&gt;We can leverage new URL () standard URL object to get pathname&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F22795bb6-17ec-4854-9179-2f8bfc9f5755" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F22795bb6-17ec-4854-9179-2f8bfc9f5755" alt="URL object pattern diagram" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Quickly copy and paste this code into the worker.ts file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;const requestUrl &lt;span class="o"&gt;=&lt;/span&gt; new URL&lt;span class="o"&gt;(&lt;/span&gt;request.url&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
const &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;hostname&lt;/span&gt;, pathname, search, &lt;span class="nb"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; requestUrl&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F79874846-9166-4243-84bc-9a92c672b5d8" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F79874846-9166-4243-84bc-9a92c672b5d8" alt="Accessing URL object values inside the worker" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can try console logging hostname, pathname, search, hash to understand URL object&lt;/p&gt;

&lt;p&gt;But, we are interested in the pathname we can check if it matches substring &lt;code&gt;/getLikes&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;likes count,&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="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;pathname.includes&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'getLikes'&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return &lt;/span&gt;new Response&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'pathname includes substring `getLikes`'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F27eef5cc-7695-4b7c-8a61-b456b82b046f" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F27eef5cc-7695-4b7c-8a61-b456b82b046f" alt="Using substring to match pathname and send custom response" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s deploy and test our worker now by visiting &lt;code&gt;/getLikes&lt;/code&gt; path&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F315665f0-eb97-4160-af17-4276bf0eb807" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F315665f0-eb97-4160-af17-4276bf0eb807" alt="Screenshot of chrome tab accessing worker https address" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! It’s working we can now respond to different paths with different responses. &lt;/p&gt;

&lt;p&gt;Let’s build on this and add two more paths &lt;code&gt;/incrementLikes&lt;/code&gt; and &lt;code&gt;/decrementLikes&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also, let’s use another key &lt;code&gt;likes_count&lt;/code&gt; to track count.&lt;/p&gt;

&lt;p&gt;Run this command to create a new key-value pair and initialize with 0.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx wrangler kv:key put &lt;span class="nt"&gt;--binding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;LIKES &lt;span class="s2"&gt;"likes_count"&lt;/span&gt; &lt;span class="s2"&gt;"0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following code to handle the other  pathnames&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="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;pathname.includes&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'incrementLikes'&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return &lt;/span&gt;new Response&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'pathname includes substring `incrementLikes`'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;pathname.includes&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'decrementLikes'&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return &lt;/span&gt;new Response&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'pathname includes substring `incrementLikes`'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can do simple addition and subtraction to the likes_count key and return the appropriate value.&lt;/p&gt;

&lt;p&gt;Also, since we want to consume it as API let’s send it as JSON response. I’m leaving the implementation up to you. Notice, that I’ve also added an &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; header in the response so that we can call this API from any domain in the browser. (to prevent CROS errors)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;const likesCount &lt;span class="o"&gt;=&lt;/span&gt; await env.LIKES.get&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'likes_count'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        const requestUrl &lt;span class="o"&gt;=&lt;/span&gt; new URL&lt;span class="o"&gt;(&lt;/span&gt;request.url&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        const &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;hostname&lt;/span&gt;, pathname, search, &lt;span class="nb"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; requestUrl&lt;span class="p"&gt;;&lt;/span&gt;

        //handle &lt;span class="sb"&gt;`&lt;/span&gt;/getLikes&lt;span class="sb"&gt;`&lt;/span&gt; request and responded with likesCount
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;pathname.includes&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'getLikes'&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            const json &lt;span class="o"&gt;=&lt;/span&gt; JSON.stringify&lt;span class="o"&gt;({&lt;/span&gt; status: 200, message: &lt;span class="sb"&gt;`&lt;/span&gt;count at &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;new&lt;/span&gt;&lt;span class="p"&gt; Date().getTime()&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;, likesCount &lt;span class="o"&gt;}&lt;/span&gt;, null, 2&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;return &lt;/span&gt;new Response&lt;span class="o"&gt;(&lt;/span&gt;json, &lt;span class="o"&gt;{&lt;/span&gt;
                headers: &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s1"&gt;'content-type'&lt;/span&gt;: &lt;span class="s1"&gt;'application/json;charset=UTF-8'&lt;/span&gt;,
                    &lt;span class="s1"&gt;'Access-Control-Allow-Origin'&lt;/span&gt;: &lt;span class="s1"&gt;'*'&lt;/span&gt;,
                &lt;span class="o"&gt;}&lt;/span&gt;,
            &lt;span class="o"&gt;})&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        //handle &lt;span class="sb"&gt;`&lt;/span&gt;/incrementLikes&lt;span class="sb"&gt;`&lt;/span&gt; request and responded with updated likesCount
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;pathname.includes&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'incrementLikes'&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nb"&gt;let &lt;/span&gt;updatedCount &lt;span class="o"&gt;=&lt;/span&gt; parseInt&lt;span class="o"&gt;(&lt;/span&gt;likesCount &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'7'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; + 1&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nb"&gt;let &lt;/span&gt;status &lt;span class="o"&gt;=&lt;/span&gt; 200&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nb"&gt;let &lt;/span&gt;message &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;count updated at &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;new&lt;/span&gt;&lt;span class="p"&gt; Date().getTime()&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            try &lt;span class="o"&gt;{&lt;/span&gt;
                await env.LIKES.put&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'likes_count'&lt;/span&gt;, updatedCount.toFixed&lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; catch &lt;span class="o"&gt;(&lt;/span&gt;error&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                console.error&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Error in incrementing likes'&lt;/span&gt;, error&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;likesCount&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    updatedCount &lt;span class="o"&gt;=&lt;/span&gt; parseInt&lt;span class="o"&gt;(&lt;/span&gt;likesCount&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
                status &lt;span class="o"&gt;=&lt;/span&gt; 500&lt;span class="p"&gt;;&lt;/span&gt;
                message &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;failed to update count error: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.stringify(error)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            const json &lt;span class="o"&gt;=&lt;/span&gt; JSON.stringify&lt;span class="o"&gt;({&lt;/span&gt; status, message, likesCount: updatedCount &lt;span class="o"&gt;}&lt;/span&gt;, null, 2&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;return &lt;/span&gt;new Response&lt;span class="o"&gt;(&lt;/span&gt;json, &lt;span class="o"&gt;{&lt;/span&gt;
                headers: &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s1"&gt;'content-type'&lt;/span&gt;: &lt;span class="s1"&gt;'application/json;charset=UTF-8'&lt;/span&gt;,
                    &lt;span class="s1"&gt;'Access-Control-Allow-Origin'&lt;/span&gt;: &lt;span class="s1"&gt;'*'&lt;/span&gt;,
                &lt;span class="o"&gt;}&lt;/span&gt;,
            &lt;span class="o"&gt;})&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        //handle &lt;span class="sb"&gt;`&lt;/span&gt;/decrementLikes&lt;span class="sb"&gt;`&lt;/span&gt; request and responded with updated likesCount
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;pathname.includes&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'decrementLikes'&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nb"&gt;let &lt;/span&gt;updatedCount &lt;span class="o"&gt;=&lt;/span&gt; parseInt&lt;span class="o"&gt;(&lt;/span&gt;likesCount &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'7'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; - 1&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nb"&gt;let &lt;/span&gt;status &lt;span class="o"&gt;=&lt;/span&gt; 200&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nb"&gt;let &lt;/span&gt;message &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;count updated at &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;new&lt;/span&gt;&lt;span class="p"&gt; Date().getTime()&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            try &lt;span class="o"&gt;{&lt;/span&gt;
                await env.LIKES.put&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'likes_count'&lt;/span&gt;, updatedCount.toFixed&lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; catch &lt;span class="o"&gt;(&lt;/span&gt;error&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                console.error&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Error in decrementing likes'&lt;/span&gt;, error&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;likesCount&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    updatedCount &lt;span class="o"&gt;=&lt;/span&gt; parseInt&lt;span class="o"&gt;(&lt;/span&gt;likesCount&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
                status &lt;span class="o"&gt;=&lt;/span&gt; 500&lt;span class="p"&gt;;&lt;/span&gt;
                message &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;failed to update count error: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.stringify(error)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            const json &lt;span class="o"&gt;=&lt;/span&gt; JSON.stringify&lt;span class="o"&gt;({&lt;/span&gt; status, message, likesCount: updatedCount &lt;span class="o"&gt;}&lt;/span&gt;, null, 2&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;return &lt;/span&gt;new Response&lt;span class="o"&gt;(&lt;/span&gt;json, &lt;span class="o"&gt;{&lt;/span&gt;
                headers: &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s1"&gt;'content-type'&lt;/span&gt;: &lt;span class="s1"&gt;'application/json;charset=UTF-8'&lt;/span&gt;,
                    &lt;span class="s1"&gt;'Access-Control-Allow-Origin'&lt;/span&gt;: &lt;span class="s1"&gt;'*'&lt;/span&gt;,
                &lt;span class="o"&gt;}&lt;/span&gt;,
            &lt;span class="o"&gt;})&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        //handle &lt;span class="s1"&gt;'*'&lt;/span&gt; requests
        const json &lt;span class="o"&gt;=&lt;/span&gt; JSON.stringify&lt;span class="o"&gt;({&lt;/span&gt; status: 404, message: &lt;span class="sb"&gt;`&lt;/span&gt;unknown/missing path&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;, null, 2&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;new Response&lt;span class="o"&gt;(&lt;/span&gt;json, &lt;span class="o"&gt;{&lt;/span&gt;
            headers: &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="s1"&gt;'content-type'&lt;/span&gt;: &lt;span class="s1"&gt;'application/json;charset=UTF-8'&lt;/span&gt;,
                &lt;span class="s1"&gt;'Access-Control-Allow-Origin'&lt;/span&gt;: &lt;span class="s1"&gt;'*'&lt;/span&gt;,
            &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="o"&gt;})&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Test the API endpoint&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s quickly deploy and test our worker endpoints&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F74d4c424-6418-4312-925b-a7be2806fcd8" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F74d4c424-6418-4312-925b-a7be2806fcd8" alt="Screenshot of chrome browser accessing /getLikes endpoint" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F701ccd6c-e142-424f-9a44-c7e621e4ca48" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F701ccd6c-e142-424f-9a44-c7e621e4ca48" alt="Screenshot of chrome browser incrementing likes by hitting /incrementLikes endpoint" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can reload the page multiple times to increment the likes’ counter &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F0e7cbe6b-2d84-4e19-9374-20687cc6bd3d" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F0e7cbe6b-2d84-4e19-9374-20687cc6bd3d" alt="Screenshot of chrome browser incrementing likes by hitting /incrementLikes endpoint multiple times" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, we  should also check the current likes count again by calling /getLikes endpoint&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F8c083f2d-0670-4217-9ac8-2843c8a17e8a" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F8c083f2d-0670-4217-9ac8-2843c8a17e8a" alt="Screenshot of browser checking likes count with /getLikes endpoint after incrementing likes" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s test the decrement counter also,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F6497d033-55d9-4725-b383-e3e70c6da75d" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F6497d033-55d9-4725-b383-e3e70c6da75d" alt="Screenshot of browser hitting /decrementLikes endpoint" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s verify by visiting /getLikes endpoint&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fdb609117-71b7-4f98-b63b-a6eab8788517" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fdb609117-71b7-4f98-b63b-a6eab8788517" alt="Screenshot of verifying like count by hitting /getLikes endpoint after decrementing likes count" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations 🎉, your API endpoints are ready! (Look how easy it was, right?) 😄&lt;/p&gt;

&lt;p&gt;Now, all you gotta do is integrate them into your client app. I’m using a React app for my portfolio website so I’ll be showing you the implementation for the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5&lt;/strong&gt;: &lt;strong&gt;Integrating API into our React app&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This part is quite straightforward, we need a Like button component which on clicking will call our CF worker /API endpoint and update the likes count on UI.&lt;/p&gt;

&lt;p&gt;Here’s a simple component with Heart Icon and Count Label. Here’s the link to the &lt;a href="https://github.com/devnrj07/neerajmukta.com/blob/d78166abab6a3fe2d3e3cce64cededc0b1549061/src/components/LikeButton.tsx#L5-L36" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;import EmptyHeart from &lt;span class="s2"&gt;"./Icons/EmptyHeart"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
import FilledHeart from &lt;span class="s2"&gt;"./Icons/FilledHeart"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

const LikeButton: React.FC&amp;lt;&lt;span class="o"&gt;{&lt;/span&gt;
  liked: boolean&lt;span class="p"&gt;;&lt;/span&gt;
  likesCount: number&lt;span class="p"&gt;;&lt;/span&gt;
  onLike: &lt;span class="o"&gt;(&lt;/span&gt;e: any&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; void&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;({&lt;/span&gt; liked, likesCount, onLike &lt;span class="o"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
    &amp;lt;div &lt;span class="nv"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"flex gap-2 items-center justify-center p-1"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;{!&lt;/span&gt;liked ? &lt;span class="o"&gt;(&lt;/span&gt;
        &amp;lt;span
          &lt;span class="nv"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"animate-pulse cursor-pointer select-none"&lt;/span&gt;
          &lt;span class="nv"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;onLike&lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &amp;lt;EmptyHeart /&amp;gt;
        &amp;lt;/span&amp;gt;
      &lt;span class="o"&gt;)&lt;/span&gt; : &lt;span class="o"&gt;(&lt;/span&gt;
        &amp;lt;span
          &lt;span class="nv"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cursor-pointer select-none animate-bounce"&lt;/span&gt;
          &lt;span class="nv"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;onLike&lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &amp;lt;FilledHeart /&amp;gt;
        &amp;lt;/span&amp;gt;
      &lt;span class="o"&gt;)}&lt;/span&gt;
      &amp;lt;h6
        &lt;span class="nv"&gt;className&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;italic text-white font-medium &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;
          liked ? &lt;/span&gt;&lt;span class="s2"&gt;"animate-[pulse_2s_ease-in_forwards]"&lt;/span&gt;&lt;span class="p"&gt; &lt;/span&gt;:&lt;span class="p"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;
        &lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;likesCount&lt;span class="o"&gt;}&lt;/span&gt;
      &amp;lt;/h6&amp;gt;
    &amp;lt;/div&amp;gt;
  &lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I created a custom hook &lt;code&gt;useLikeButton&lt;/code&gt; to abstract the API logic on how I call the API and update the state, you can see the source code &lt;a href="https://github.com/devnrj07/neerajmukta.com/blob/d78166abab6a3fe2d3e3cce64cededc0b1549061/src/hooks/useLikeButton.ts#L9-L53" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;const useLikeButton &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  const &lt;span class="o"&gt;[&lt;/span&gt;isLiked, setIsLiked] &lt;span class="o"&gt;=&lt;/span&gt; useState&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  const &lt;span class="o"&gt;[&lt;/span&gt;likesCount, setLikesCount] &lt;span class="o"&gt;=&lt;/span&gt; useState&lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  useEffect&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;(&lt;/span&gt;async &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      const data &lt;span class="o"&gt;=&lt;/span&gt; await getLikesCount&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;data &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; data.status &lt;span class="o"&gt;===&lt;/span&gt; 200&lt;span class="o"&gt;)&lt;/span&gt; setLikesCount&lt;span class="o"&gt;(&lt;/span&gt;parseInt&lt;span class="o"&gt;(&lt;/span&gt;data.likesCount&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;})()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;isLiked]&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  const handleLikeButtonClick &lt;span class="o"&gt;=&lt;/span&gt; async &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    setIsLiked&lt;span class="o"&gt;(!&lt;/span&gt;isLiked&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    try &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;isLiked&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        const data &lt;span class="o"&gt;=&lt;/span&gt; await decrementLike&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;data &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; data.status &lt;span class="o"&gt;===&lt;/span&gt; 200&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          console.log&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"decrement success"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          setLikesCount&lt;span class="o"&gt;(&lt;/span&gt;parseInt&lt;span class="o"&gt;(&lt;/span&gt;data.likesCount&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        const data &lt;span class="o"&gt;=&lt;/span&gt; await incrementLike&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;data &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; data.status &lt;span class="o"&gt;===&lt;/span&gt; 200&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          console.log&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"increment success"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          setLikesCount&lt;span class="o"&gt;(&lt;/span&gt;parseInt&lt;span class="o"&gt;(&lt;/span&gt;data.likesCount&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; catch &lt;span class="o"&gt;(&lt;/span&gt;err&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      //roll back
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;isLiked&lt;span class="o"&gt;)&lt;/span&gt; setLikesCount&lt;span class="o"&gt;((&lt;/span&gt;prev&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; prev + 1&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;else &lt;/span&gt;setLikesCount&lt;span class="o"&gt;((&lt;/span&gt;prev&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; prev - 1&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    isLiked,
    likesCount,
    handleLikeButtonClick,
  &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;incrementLike&lt;/code&gt; and &lt;code&gt;decrementLike&lt;/code&gt; functions look like this again check the &lt;a href="https://github.com/devnrj07/neerajmukta.com/blob/d78166abab6a3fe2d3e3cce64cededc0b1549061/src/utils/APIRequests/index.ts#L1-L14" rel="noopener noreferrer"&gt;source code&lt;/a&gt; for more details. &lt;/p&gt;

&lt;p&gt;We only need a GET request here because we are not sending any payload to the worker but you can create POST request too with CloudFlare workers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;import &lt;span class="o"&gt;{&lt;/span&gt; ADD_LIKE, ADD_UNLIKE, GET_LIKES_COUNT &lt;span class="o"&gt;}&lt;/span&gt; from &lt;span class="s2"&gt;"../constants"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;const GET &lt;span class="o"&gt;=&lt;/span&gt; async &lt;span class="o"&gt;(&lt;/span&gt;url: string&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  try &lt;span class="o"&gt;{&lt;/span&gt;
    const response &lt;span class="o"&gt;=&lt;/span&gt; await &lt;span class="o"&gt;(&lt;/span&gt;await fetch&lt;span class="o"&gt;(&lt;/span&gt;url&lt;span class="o"&gt;))&lt;/span&gt;.json&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;response&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt; catch &lt;span class="o"&gt;(&lt;/span&gt;err&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    console.error&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Oops! Failed to get likes"&lt;/span&gt;, err&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;const getLikesCount &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; GET&lt;span class="o"&gt;(&lt;/span&gt;GET_LIKES_COUNT&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;const incrementLike &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; GET&lt;span class="o"&gt;(&lt;/span&gt;ADD_LIKE&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;const decrementLike &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; GET&lt;span class="o"&gt;(&lt;/span&gt;ADD_UNLIKE&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, I added the &lt;code&gt;LikeButton&lt;/code&gt; component to the page and passed the necessary props to it from our custom hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;const &lt;span class="o"&gt;{&lt;/span&gt; isLiked, likesCount, handleLikeButtonClick &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; useLikeButton&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
...
//above code hidden &lt;span class="k"&gt;for &lt;/span&gt;brevity

&amp;lt;LikeButton
          &lt;span class="nv"&gt;liked&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;isLiked&lt;span class="o"&gt;}&lt;/span&gt;
          &lt;span class="nv"&gt;likesCount&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;likesCount&lt;span class="o"&gt;}&lt;/span&gt;
          &lt;span class="nv"&gt;onLike&lt;/span&gt;&lt;span class="o"&gt;={&lt;/span&gt;handleLikeButtonClick&lt;span class="o"&gt;}&lt;/span&gt;
        /&amp;gt;

&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is it! We should now be able to see it in action.&lt;/p&gt;

&lt;p&gt;There’s one problem though, since we don’t have user identity attached to our likes count data we cannot tell if the user has already liked our page. This could result in the same user liking it multiple times because the button resets every time the page is refreshed or revisted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxgapww51a13bjwzte0w.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frxgapww51a13bjwzte0w.gif" alt="Like button without localstorage" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As you can tell, this happens because our state gets reset inside React app.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 6&lt;/strong&gt;: &lt;strong&gt;Persistence problem on the client side&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One simple way to fix this issue is to use the browser’s local storage. &lt;/p&gt;

&lt;p&gt;We will set a flag (’has-like’) in local storage and based on that update the button state. &lt;/p&gt;

&lt;p&gt;So, whenever a user visits our page we first check if that particular key is present or not&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the flag is present we keep the button state as liked and when the user clicks the button we decrement it and change the state to unlike.&lt;/li&gt;
&lt;li&gt;if it's not, we keep the button state as unlike, and when the user clicks the button we increment it and change the state to like.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 7&lt;/strong&gt;: &lt;strong&gt;Adding Local storage to persist state locally&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’ll use this simple custom hook &lt;code&gt;useLocalStorage&lt;/code&gt; to set/get local storage data, the signature will be very similar to React’s &lt;code&gt;useState&lt;/code&gt; hook and it will be interchangeable.&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;type &lt;/span&gt;SetValue&amp;lt;T&amp;gt; &lt;span class="o"&gt;=&lt;/span&gt; Dispatch&amp;lt;SetStateAction&amp;lt;T&amp;gt;&amp;gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="k"&gt;function &lt;/span&gt;useLocalStorage&amp;lt;T&amp;gt;&lt;span class="o"&gt;(&lt;/span&gt;
  key: string,
  initialValue: T
&lt;span class="o"&gt;)&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;T, SetValue&amp;lt;T&amp;gt;] &lt;span class="o"&gt;{&lt;/span&gt;
  const readValue &lt;span class="o"&gt;=&lt;/span&gt; useCallback&lt;span class="o"&gt;(()&lt;/span&gt;: T &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;typeof window &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;"undefined"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;initialValue&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    try &lt;span class="o"&gt;{&lt;/span&gt;
      const item &lt;span class="o"&gt;=&lt;/span&gt; window.localStorage.getItem&lt;span class="o"&gt;(&lt;/span&gt;key&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;item ? &lt;span class="o"&gt;(&lt;/span&gt;parseJSON&lt;span class="o"&gt;(&lt;/span&gt;item&lt;span class="o"&gt;)&lt;/span&gt; as T&lt;span class="o"&gt;)&lt;/span&gt; : initialValue&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; catch &lt;span class="o"&gt;(&lt;/span&gt;error&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      console.warn&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;Error reading localStorage key “&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;”:&lt;span class="sb"&gt;`&lt;/span&gt;, error&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;initialValue&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;, &lt;span class="o"&gt;[&lt;/span&gt;initialValue, key]&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  // State to store our value
  // Pass initial state &lt;span class="k"&gt;function &lt;/span&gt;to useState so logic is only executed once
  const &lt;span class="o"&gt;[&lt;/span&gt;storedValue, setStoredValue] &lt;span class="o"&gt;=&lt;/span&gt; useState&amp;lt;T&amp;gt;&lt;span class="o"&gt;(&lt;/span&gt;readValue&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  const setValue: SetValue&amp;lt;T&amp;gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;value&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    // Prevent build error &lt;span class="s2"&gt;"window is undefined"&lt;/span&gt; but keeps working
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;typeof window &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;"undefined"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      console.warn&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="sb"&gt;`&lt;/span&gt;Tried setting localStorage key “&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;” even though environment is not a client&lt;span class="sb"&gt;`&lt;/span&gt;
      &lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    try &lt;span class="o"&gt;{&lt;/span&gt;
      // Allow value to be a &lt;span class="k"&gt;function &lt;/span&gt;so we have the same API as useState
      const newValue &lt;span class="o"&gt;=&lt;/span&gt; value instanceof Function ? value&lt;span class="o"&gt;(&lt;/span&gt;storedValue&lt;span class="o"&gt;)&lt;/span&gt; : value&lt;span class="p"&gt;;&lt;/span&gt;
      window.localStorage.setItem&lt;span class="o"&gt;(&lt;/span&gt;key, JSON.stringify&lt;span class="o"&gt;(&lt;/span&gt;newValue&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      setStoredValue&lt;span class="o"&gt;(&lt;/span&gt;newValue&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; catch &lt;span class="o"&gt;(&lt;/span&gt;error&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      console.warn&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;Error setting localStorage key “&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;”:&lt;span class="sb"&gt;`&lt;/span&gt;, error&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  useEffect&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    setStoredValue&lt;span class="o"&gt;(&lt;/span&gt;readValue&lt;span class="o"&gt;())&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  &lt;span class="o"&gt;}&lt;/span&gt;, &lt;span class="o"&gt;[])&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;storedValue, setValue]&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

// A wrapper &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s2"&gt;"JSON.parse()"" to support "&lt;/span&gt;undefined&lt;span class="s2"&gt;" value
function parseJSON&amp;lt;T&amp;gt;(value: string | null): T | undefined {
  try {
    return value === "&lt;/span&gt;undefined&lt;span class="s2"&gt;" ? undefined : JSON.parse(value ?? "");
  } catch {
    console.log("&lt;/span&gt;parsing error on&lt;span class="s2"&gt;", { value });
    return undefined;
  }
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One last step, We just need to replace useState inside our custom hook with this local storage hook&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fd3b9a2f5-de11-4199-bcc7-5c38f8fcb0a0" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2Fd3b9a2f5-de11-4199-bcc7-5c38f8fcb0a0" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F9025afab-9df7-40bb-b5ee-04630f4e36ce" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblog-images-prod.s3.amazonaws.com%2F9025afab-9df7-40bb-b5ee-04630f4e36ce" alt="Untitled.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time to test.&lt;/strong&gt; Let’s refresh the page and click the like button then refresh the page again.&lt;/p&gt;

&lt;p&gt;Voila, the button is still in a like state.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcwvp07g9migpbdoxlxwy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcwvp07g9migpbdoxlxwy.gif" alt="Like button with localstorage" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Phew! That seemed like a lot of work but trust me we saved tons of time in API development for simple use cases like this.&lt;/p&gt;

&lt;p&gt;Another thing to note here is that Vercel also provides similar &lt;a href="https://vercel.com/docs/storage/vercel-kv" rel="noopener noreferrer"&gt;KV storage&lt;/a&gt; so we can implement the same APIs using &lt;a href="https://vercel.com/docs/functions/edge-functions" rel="noopener noreferrer"&gt;Vercel Edge Functions&lt;/a&gt; also. &lt;/p&gt;

&lt;p&gt;Let me know in the comments if you would like me to write a blog on that as well or just comment on how you liked this one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our Learnings from this blog post:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to create an API endpoint using CloudFlare worker, ✅&lt;/li&gt;
&lt;li&gt;How to use key-value storage for data persistence in our APIs ✅&lt;/li&gt;
&lt;li&gt;how to save cost by not booting up an EC2 instance for simple APIs. ✅ 🤑&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please do visit my &lt;a href="https://neerajmukta.com/" rel="noopener noreferrer"&gt;website&lt;/a&gt; and drop a like. This post was published using a cross-platform publishing tool &lt;a href="https://buzpen.com/" rel="noopener noreferrer"&gt;Buzpen&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>serverless</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Serverless Functions vs Edge Functions vs Cloudflare workers</title>
      <dc:creator>Neeraj Mukta</dc:creator>
      <pubDate>Wed, 23 Aug 2023 05:43:47 +0000</pubDate>
      <link>https://dev.to/fullstacknrj/serverless-functions-vs-edge-functions-vs-cloudflare-workers-386p</link>
      <guid>https://dev.to/fullstacknrj/serverless-functions-vs-edge-functions-vs-cloudflare-workers-386p</guid>
      <description>&lt;p&gt;Let’s quickly understand the differences between these 3 server-less technologies. If you are like me and have been too busy to get a glimpse of the whole server-less hype, stay tuned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are&lt;/strong&gt; &lt;strong&gt;Serverless Functions?&lt;/strong&gt;&lt;br&gt;
As the name suggests they are functions that can run without requiring a dedicated server. Think of these serverless functions as a small unit of code that runs in response to specific events, usually triggered by HTTP requests. The logic behind the creation of this technology is to save long-running server costs for trivial and independent tasks. They are hosted on cloud platforms like AWS Lambda, Google Cloud Functions, or Azure Functions. These serverless functions are highly scalable, cost-effective, and easy to deploy. These functions are ideal for creating backend services and APIs, and they excel at handling specific tasks triggered by events.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are the usecases for serverless functions?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;REST APIs for your frontend/client apps&lt;/li&gt;
&lt;li&gt;event driven tasks or cron jobs, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Uber&lt;/strong&gt; uses serverless functions to handle various backend tasks, such as processing ride requests, calculating fares, and sending notifications to drivers and passengers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Airbnb&lt;/strong&gt; leverages serverless functions for managing user authentication, handling reservations, and sending booking confirmation emails.&lt;/li&gt;
&lt;li&gt;we can build an &lt;strong&gt;image thumbnail generator&lt;/strong&gt; that processes uploaded images and generates thumbnails on-the-fly when requested via API endpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What are Edge Functions?&lt;/strong&gt;&lt;br&gt;
Edge Functions are very similar to serverless functions but they run in a browser environment (chrome V8 engine) meaning they support only browser APIs and you cannot use native Node APIs/packages inside it. Edge functions run on content delivery networks (CDNs) at the network edge, close to the user's location. They are designed to manipulate and optimize content delivery. Edge functions enhance performance by reducing latency and can be used for tasks such as caching, content transformation, and security enhancements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where should I use them?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to Implement real-time image optimization that resizes images based on the user's device and network conditions before delivering them, ensuring fast loading times.&lt;/li&gt;
&lt;li&gt;to redirect users to a different website, etc.&lt;/li&gt;
&lt;li&gt;rate limiting,&lt;/li&gt;
&lt;li&gt;geolocation based redirects/responses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Netflix&lt;/strong&gt; uses edge functions to optimize content delivery by dynamically selecting the appropriate bitrate based on the viewer's device and network conditions, ensuring smooth streaming experiences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pinterest&lt;/strong&gt; utilizes edge functions to resize and optimize images on-the-fly, delivering them in the optimal format and size for the user's device.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What are Cloudflare Workers?&lt;/strong&gt;&lt;br&gt;
Cloudflare Workers are a specific implementation of edge functions offered by Cloudflare. They allow you to write JavaScript code that runs at Cloudflare's edge servers. Cloudflare Workers are known for their flexibility, low-latency execution, and can be used for tasks such as request handling, routing, and filtering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where should I use them?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a custom bot detection and mitigation system that analyzes incoming traffic at the edge, blocking or challenging requests that exhibit suspicious behavior.&lt;/li&gt;
&lt;li&gt;reverse proxy the traffic&lt;/li&gt;
&lt;li&gt;A/B testing, etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Discord&lt;/strong&gt; employs Cloudflare Workers to enhance the security of their platform by implementing rate limiting, DDoS protection, and IP filtering at the edge, minimizing the impact of malicious traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coinbase&lt;/strong&gt; uses Cloudflare Workers to manage traffic and routing, ensuring that users are directed to the appropriate server instances for trading, account management, and data retrieval.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for reading! Hope you learned something. You can check out my work and connect with me &lt;a href="https://neerajmukta.com" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
This blog was published to medium, devto, hashnode using &lt;a href="https://buzpen.com" rel="noopener noreferrer"&gt;buzpen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;References :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.splunk.com/en_us/blog/learn/serverless-functions.html" rel="noopener noreferrer"&gt;https://www.splunk.com/en_us/blog/learn/serverless-functions.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.netlify.com/blog/edge-functions-explained/" rel="noopener noreferrer"&gt;https://www.netlify.com/blog/edge-functions-explained/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.cloudflare.com/introducing-cloudflare-workers/" rel="noopener noreferrer"&gt;https://blog.cloudflare.com/introducing-cloudflare-workers/&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Learn to render Maps by building a wildfire tracker app - Part 2</title>
      <dc:creator>Neeraj Mukta</dc:creator>
      <pubDate>Sun, 03 Jan 2021 19:28:41 +0000</pubDate>
      <link>https://dev.to/fullstacknrj/learn-to-render-maps-by-building-a-wildfire-tracker-app-part-2-go0</link>
      <guid>https://dev.to/fullstacknrj/learn-to-render-maps-by-building-a-wildfire-tracker-app-part-2-go0</guid>
      <description>&lt;p&gt;This is part two of &lt;strong&gt;Learn to render Maps by building a wildfire tracker app&lt;/strong&gt; series. If you missed the part one, you can read it &lt;a href="https://dev.to/devnrj07/learn-to-render-maps-by-building-a-wildfire-tracker-app-part-1-24j5"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, the complete source code is &lt;a href="https://github.com/devnrj07/cool-react-projects/tree/master/wildfire-tracker-app" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the first part we rendered a simple map using leaflet now we'll plot some data (wildfire data) on this map. We're going to use this &lt;a href="https://eonet.sci.gsfc.nasa.gov/api/v2.1/events" rel="noopener noreferrer"&gt;NASA API&lt;/a&gt;. &lt;br&gt;
This is an opensource API which requires no key. You can checkout &lt;a href="https://api.nasa.gov/" rel="noopener noreferrer"&gt;NASA website&lt;/a&gt; from information about their cool Apis.&lt;/p&gt;

&lt;p&gt;Now, let's fetch data from the api there are many ways/packages to make http requests but I'm going to use fetch as it is directly available in the modern browsers. &lt;br&gt;
We'll use two common React hooks &lt;strong&gt;useState&lt;/strong&gt; and &lt;strong&gt;useEffect&lt;/strong&gt; to make an API request and set the response data.&lt;/p&gt;

&lt;p&gt;Add the following code in the &lt;strong&gt;app.js&lt;/strong&gt; file inside src directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   const [eventData, setEventData] = useState([])
   const [loading, setLoading] = useState(false)
   useEffect(()=&amp;gt;{
      const fetchEvents = async () =&amp;gt;{
         setLoading(true)
         const {events} = await (await 
  fetch('https://eonet.sci.gsfc.nasa.gov/api/v2.1/events')).json()
         setEventData(events)
         setLoading(false)
       }
       fetchEvents()
    }, [])

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

&lt;/div&gt;



&lt;p&gt;This is simple react stuff making api call, setting state and if you find it difficult to understand this I recommend reading the &lt;a href="https://reactjs.org/docs/hooks-reference.html" rel="noopener noreferrer"&gt;react docs&lt;/a&gt; and comeback here again!&lt;/p&gt;

&lt;p&gt;Now, we'll modify the &lt;strong&gt;Map&lt;/strong&gt; component from previous post to take &lt;em&gt;eventData&lt;/em&gt; as a &lt;em&gt;prop&lt;/em&gt; and also, we'll display a &lt;em&gt;loader&lt;/em&gt; until the api returns the response and state has been set. &lt;/p&gt;

&lt;p&gt;This how our &lt;strong&gt;app.js&lt;/strong&gt; will look like now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   import Map from "./components/Map";
   import "./App.css";
   import { useState, useEffect } from "react";
   import Header from './components/Header';
   import Loader from './components/Loader';

  function App() {
    const [eventData, setEventData] = useState([])
    const [loading, setLoading] = useState(false)
    useEffect(()=&amp;gt;{
    const fetchEvents = async () =&amp;gt;{
       setLoading(true)
       const {events} = await (await 
  fetch('https://eonet.sci.gsfc.nasa.gov/api/v2.1/events')).json()
       setEventData(events)
       setLoading(false)
       }
      fetchEvents()
    }, [])

   return (
     &amp;lt;div&amp;gt;
       &amp;lt;Header /&amp;gt;
       { !loading ? &amp;lt;Map eventData={eventData} /&amp;gt; : &amp;lt;Loader /&amp;gt; }
     &amp;lt;/div&amp;gt;
   );
 }

 export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt; : const {events} = await (await fetch('&lt;a href="https://eonet.sci.gsfc.nasa.gov/api/v2.1/events')).json(" rel="noopener noreferrer"&gt;https://eonet.sci.gsfc.nasa.gov/api/v2.1/events')).json(&lt;/a&gt;)&lt;br&gt;
This is called object destructing, more &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment" rel="noopener noreferrer"&gt;details&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And this our simple &lt;strong&gt;Loader&lt;/strong&gt; component. You can get that gif file from &lt;a href="https://github.com/devnrj07/cool-react-projects/tree/master/wildfire-tracker-app" rel="noopener noreferrer"&gt;source code&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  import fireLoader from './fireLoader.gif'

   const Loader = () =&amp;gt; {
      return (
          &amp;lt;div className="loader"&amp;gt;
             &amp;lt;img src={fireLoader} alt='fireLoader'/&amp;gt;
              &amp;lt;h2&amp;gt;Loading ...&amp;lt;/h2&amp;gt;
          &amp;lt;/div&amp;gt;
      )
   }

   export default Loader;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, Now let's look at the response and we can see that it has an events array which includes information about different natural events like Storms, wildfire, cyclones etc.&lt;br&gt;
But, we only need wildfire co-ordinates from the events array which has &lt;strong&gt;id&lt;/strong&gt; as &lt;strong&gt;8&lt;/strong&gt; inside the &lt;strong&gt;categories&lt;/strong&gt; object. &lt;br&gt;
&lt;em&gt;So, we need to filter out all the objects whose &lt;strong&gt;id&lt;/strong&gt; is &lt;strong&gt;8&lt;/strong&gt; inside the &lt;strong&gt;categories&lt;/strong&gt; object&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3oyzy4hbquuvn9wfyh70.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3oyzy4hbquuvn9wfyh70.png" alt="NASA API Response" width="800" height="699"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's now update our &lt;strong&gt;Map&lt;/strong&gt; component a bit to use &lt;em&gt;eventData&lt;/em&gt; prop, add the following code to the Map component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   // define constants
   const NATURAL_EVENT_WILDFIRE = 8;

     const Map = ({ center, zoom, eventData }) =&amp;gt; {
     const markers = eventData.map((event, key) =&amp;gt; {
        if (event.categories[0].id === NATURAL_EVENT_WILDFIRE) {
          const [lng, lat] = event.geometries[0].coordinates;
       return (
         &amp;lt;LocationMarker key={key} lng={lng} lat={lat} info= 
          {event.title} /&amp;gt;
         );
        }
     });

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

&lt;/div&gt;



&lt;p&gt;As you can notice we're looping through the &lt;em&gt;eventData&lt;/em&gt; array and looking for objects with id equals to 8 and get the &lt;em&gt;longitute&lt;/em&gt; and &lt;em&gt;latitude&lt;/em&gt; array as well as event &lt;em&gt;title&lt;/em&gt; to display the info about the wildfire when we click on any of the markers.&lt;br&gt;
That's it now we just render the &lt;em&gt;markers&lt;/em&gt; array which is an array of all the Location Markers. So, now the complete code for our &lt;strong&gt;Map&lt;/strong&gt; component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   import React from "react";
   import { MapContainer, TileLayer } from "react-leaflet";
   import LocationMarker from "./LocationMarker";

   // define constants
   const NATURAL_EVENT_WILDFIRE = 8;

   const Map = ({ center, zoom, eventData }) =&amp;gt; {
      const markers = eventData.map((event, key) =&amp;gt; {
       if (event.categories[0].id === NATURAL_EVENT_WILDFIRE) {
          const [lng, lat] = event.geometries[0].coordinates;
        return (
         &amp;lt;LocationMarker key={key} lng={lng} lat={lat} info= 
          {event.title} /&amp;gt;
        );
      }
    });

    return (
       &amp;lt;MapContainer style={{ height: "100vh" }} center={center} 
           zoom={zoom}&amp;gt;
          &amp;lt;TileLayer
            attribution='&amp;amp;copy; &amp;lt;a 
             href="http://osm.org/copyright"&amp;gt;OpenStreetMap&amp;lt;/a&amp;gt; 
             contributors'
          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
         /&amp;gt;
        {markers}
      &amp;lt;/MapContainer&amp;gt;
    );
   };

   Map.defaultProps = {
      center: [42.3265, -122.8756],
      zoom: 6,
   };

   export default Map;

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

&lt;/div&gt;



&lt;p&gt;This is it our app is ready. Start the app using &lt;br&gt;
  &lt;code&gt;yarn start&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsrhwe9rs9h7u6ecr2llb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsrhwe9rs9h7u6ecr2llb.gif" alt="Final App Demo" width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is so much you could do with leaflet, you can change tiles, provide navigation and much more and the best part is it's open source. Possibilities are limitless.&lt;/p&gt;

&lt;p&gt;I hope you liked this series and if you did please share and follow me &lt;a href="https://dev.to/devnrj07"&gt;devnrj07&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have a Happy and Covid-free New Year! &lt;/p&gt;

</description>
      <category>react</category>
      <category>reactleaflet</category>
      <category>leaflet</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Learn to render Maps by building a wildfire tracker app - Part 1</title>
      <dc:creator>Neeraj Mukta</dc:creator>
      <pubDate>Fri, 01 Jan 2021 07:07:59 +0000</pubDate>
      <link>https://dev.to/fullstacknrj/learn-to-render-maps-by-building-a-wildfire-tracker-app-part-1-24j5</link>
      <guid>https://dev.to/fullstacknrj/learn-to-render-maps-by-building-a-wildfire-tracker-app-part-1-24j5</guid>
      <description>&lt;p&gt;Recently, I was working on a project where I needed to use Maps to display and pinpoint information about goods. So, I started out to look for MAP libraries my first choice was obviously Google Maps API which is very simple, has a good documentation and a very nice &lt;strong&gt;react-wrapper&lt;/strong&gt;. But, the downside is, it’s quite tricky to set it up, you need to setup billing account and some other prerequisites until you see a map. &lt;/p&gt;

&lt;p&gt;This is going to be a two part series :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/devnrj07/learn-to-render-maps-by-building-a-wildfire-tracker-app-part-1-24j5"&gt;Part one  : about leaflet and rendering Map&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/devnrj07/learn-to-render-maps-by-building-a-wildfire-tracker-app-part-2-go0"&gt;Part two : use Nasa Api to display wildfires on the map&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3upb7dltujmyas4zwjgt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3upb7dltujmyas4zwjgt.png" alt="Google map dev mode issue" width="575" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  This screen annoyed the heck out of me!
&lt;/h4&gt;

&lt;p&gt;So, after some more research I landed on this open source library which also has react-wrapper and it's called &lt;a href="https://leafletjs.com/" rel="noopener noreferrer"&gt;leaflet&lt;/a&gt;&lt;br&gt;
It's very useful easy to setup library but it still has some caveats to it. So, I'm writing this blog to help beginners move in the right direction with this library. &lt;br&gt;
I'm going to show how to make it work with react project for which I will use the following libraries :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://create-react-app.dev/" rel="noopener noreferrer"&gt;CRA&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/leaflet" rel="noopener noreferrer"&gt;leaflet&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/react-leaflet" rel="noopener noreferrer"&gt;react-leaflet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Let's quickly generate react project using create-react-app. Open a terminal and run the following command :&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx create-react-app map-demo-app&lt;/code&gt;&lt;br&gt;
This will generate a basic react app with all the necessary files.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Let's also add leaflet libraries to out project. Type the following command in your terminal&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd map-demo-app &amp;amp;&amp;amp; yarn add leaflet react-leaflet&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, we're all set to render our map. For this let's make a &lt;strong&gt;Map&lt;/strong&gt; component.&lt;br&gt;
To render the map we need to follow three simple steps :&lt;br&gt;&lt;br&gt;
i. import leaflet css in your index.js file&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import 'leaflet/dist/leaflet.css';&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;ii. create a file call &lt;em&gt;Map.js&lt;/em&gt; inside src and add this &lt;br&gt;
  piece of code. Note the change in react-leaflet api (its &lt;br&gt;
  MapContainer instead of Map)&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   import { MapContainer, TileLayer } from "react-leaflet";
   const Map = ({ center, zoom }) =&amp;gt; {
   return (
     &amp;lt;MapContainer style={{ height: "100vh" }} center={center} 
        zoom={zoom}&amp;gt;
      &amp;lt;TileLayer
          attribution='&amp;amp;copy; &amp;lt;a 
          href="http://osm.org/copyright"&amp;gt;OpenStreetMap&amp;lt;/a&amp;gt; 
          contributors'
      url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
      /&amp;gt;
   &amp;lt;/MapContainer&amp;gt;
   );
  };
  Map.defaultProps = {
   center: [42.3265, -122.8756],
   zoom: 6,
  };
  export default Map;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;iii. call this component inside your app.js file&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Map from "./components/Map";
function App() {
 return (
     &amp;lt;div&amp;gt;
      &amp;lt;Map/&amp;gt;
    &amp;lt;/div&amp;gt;
   );
 }
export default App;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;Great! Now you should be able to see a map like this after starting the project &lt;br&gt;
&lt;code&gt;yarn start&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6p19du9lkljgofdkdq24.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6p19du9lkljgofdkdq24.png" alt="Plain Map" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, Let's add some markers to our map. For this we need to use &lt;strong&gt;Marker&lt;/strong&gt; component from react-leaflet.&lt;br&gt;
Add the following code to a new component and name it something like &lt;strong&gt;LocationMarker.js&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  import {Marker} from  'react-leaflet';
  const LocationMarker = ({ lat, lng}) =&amp;gt; {
     return (
        &amp;lt;Marker position={[lat, lng]}&amp;gt;
        &amp;lt;/Marker&amp;gt;
     );
   };
   LocationMarker.defaultProps = {
     lat: 42.3265,
     lng: -122.8756,
   };
export default LocationMarker;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice, how I pass &lt;strong&gt;latitude&lt;/strong&gt; and &lt;strong&gt;longitude&lt;/strong&gt; as an array to the &lt;em&gt;position&lt;/em&gt; prop of &lt;strong&gt;Marker&lt;/strong&gt; component.&lt;/p&gt;

&lt;p&gt;Wait but we're not done yet, there's still an issue which I faced after this. The default icon wasn't getting displayed on the map, it was just an empty image placeholder.&lt;/p&gt;

&lt;p&gt;The workaround for this is however very simple, all we need to do is provide a defaultIcon ourselves.&lt;br&gt;
Quickly add these lines of code to our &lt;strong&gt;LocationMarker&lt;/strong&gt; component&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  import L from "leaflet";
  import Icon from "leaflet/dist/images/marker-icon.png";
  import iconShadow from "leaflet/dist/images/marker-shadow.png";

  let DefaultIcon = L.icon({
     iconUrl: Icon,
     shadowUrl: iconShadow,
     iconSize: [32, 32],
     shadowSize: [28, 28],
});

**L.Marker.prototype.options.icon = DefaultIcon**;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should solve the problem of missing icon image on the map. And now, you should be able to see the default icon on the map at co-ordinates = [42.3265,-122.8756].&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmora5q9xg7d7wcqfjm57.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmora5q9xg7d7wcqfjm57.png" alt="Map with marker" width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can also add a popup on the marker to display some information about that position on the map.&lt;br&gt;
Quickly, update the &lt;strong&gt;LocationMarker&lt;/strong&gt; component with these lines of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
  import {Marker, Popup} from 'react-leaflet';
  const LocationMarker = ({ lat, lng, info}) =&amp;gt; {
     return (
        &amp;lt;Marker position={[lat, lng]}&amp;gt;
         &amp;lt;Popup&amp;gt; {info} &amp;lt;/Popup&amp;gt;
        &amp;lt;/Marker&amp;gt;
     );
  };
  LocationMarker.defaultProps = {
   lat: 42.3265,
   lng: -122.8756,
   info : 'I love leaflet &amp;lt;3'
 };
export default LocationMarker;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsklbzuxur1t612eevy0y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsklbzuxur1t612eevy0y.png" alt="marker with popup" width="800" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you face any problem related to this post than comment below.&lt;br&gt;
And Stay tuned for next part of this blog series.&lt;br&gt;
Thank you.&lt;/p&gt;

</description>
      <category>react</category>
      <category>leaflet</category>
      <category>reacthooks</category>
      <category>reactleaflet</category>
    </item>
  </channel>
</rss>
