<?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: Samuel Umoren</title>
    <description>The latest articles on DEV Community by Samuel Umoren (@umoren).</description>
    <link>https://dev.to/umoren</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%2F137857%2Ffccda3fc-1ef3-4838-888f-c4986d18123d.jpeg</url>
      <title>DEV Community: Samuel Umoren</title>
      <link>https://dev.to/umoren</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/umoren"/>
    <language>en</language>
    <item>
      <title>How I Stopped Chasing Clients and Started Building Assets: A Technical Writer's Guide to Multiple Income Streams</title>
      <dc:creator>Samuel Umoren</dc:creator>
      <pubDate>Tue, 08 Jul 2025 14:12:59 +0000</pubDate>
      <link>https://dev.to/umoren/how-i-stopped-chasing-clients-and-started-building-assets-a-technical-writers-guide-to-multiple-2hlg</link>
      <guid>https://dev.to/umoren/how-i-stopped-chasing-clients-and-started-building-assets-a-technical-writers-guide-to-multiple-2hlg</guid>
      <description>&lt;p&gt;In 2020, I was in my final year at university, working as a frontend engineer at a fintech startup for 30k naira per month. My friend and I needed extra income and were literally hungry. Then we found this Indian blogger running &lt;a href="https://codesource.com/" rel="noopener noreferrer"&gt;Codesource&lt;/a&gt; who paid $10 per frontend tutorial. That $10 was significant money for us.&lt;/p&gt;

&lt;p&gt;But here's what changed things for me: I didn't jump into freelancing cold. I'd been building what we call "career capital" by writing my own content on &lt;a href="https://medium.com/@umorensamuel" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;, &lt;a href="https://dev.to/umoren"&gt;Dev.to&lt;/a&gt;, and &lt;a href="https://beyondcode.hashnode.dev/" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;. Free content that showed I could write technical tutorials.&lt;/p&gt;

&lt;p&gt;That mindset shift from "I need money now" to "I need to build assets first" really helped my trajectory. Fast forward to today, I'm working as an AI developer, still earning from technical writing, and speaking about this journey. Not because I figured it all out, but because I know how much these strategies can help when you're starting from survival mode.&lt;/p&gt;

&lt;p&gt;Here's what I've learned about building multiple income streams as a technical writer. Some of it worked, some of it didn't, but all of it taught me something.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Had to Think Beyond the 9-to-5
&lt;/h2&gt;

&lt;p&gt;The biggest shift that helped me was realizing you have to build the asset before you monetize it. Most people want to get paid immediately, but I learned you need to prove you can deliver value first, then people will pay you for it.&lt;/p&gt;

&lt;p&gt;This same principle applied when I moved into AI development. I built a bunch of &lt;a href="https://github.com/Umoren" rel="noopener noreferrer"&gt;projects&lt;/a&gt; first, documented everything, then used that as my transition path. The documentation work I'd been doing suddenly became more valuable because I understood the technology I was writing about.&lt;/p&gt;

&lt;p&gt;Career capital isn't just about portfolio pieces. It's about developing the skills and reputation that make opportunities come to you instead of you chasing them constantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Writing Areas That Actually Pay Well
&lt;/h2&gt;

&lt;p&gt;Three areas are really hot right now:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Developer documentation&lt;/strong&gt; is finally getting the respect it deserves. Companies understand that bad docs kill adoption. This is why programs like WriteTech Hub exist. When I started in 2020, we were just writing blogs. Now there's serious demand for API docs, SDK guides, and integration tutorials.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI tools and workflows&lt;/strong&gt; are everywhere. For the last few months, every client article I've written has been AI-focused. Agents, MCP (Model Context Protocol), RAG implementations. You need to stay with the trends because that's literally part of the job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloud-native tools&lt;/strong&gt; keep evolving. Cloud management, containerization, DevOps tooling. This demand stays consistent because the ecosystem keeps changing.&lt;/p&gt;

&lt;p&gt;What I've learned about showing your skills in these areas:&lt;/p&gt;

&lt;p&gt;Build public examples. Don't just say you can write API docs, actually document an API. Pick a trending AI tool and write a tutorial about it. Contribute to open source documentation.&lt;/p&gt;

&lt;p&gt;The rates I've seen: Content writing ranges from $100-600 per article depending on complexity. Core documentation work varies by company, but it's significantly higher than blog rates.&lt;/p&gt;

&lt;p&gt;Companies paying top rates want writers who understand the technology they're documenting. That's why my AI transition made my writing more valuable. I can explain what I've actually built.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to Find Real Freelance Opportunities
&lt;/h2&gt;

&lt;p&gt;I'll be straight with you. I haven't had success with Fiverr or Upwork. But here are the four channels that built my career:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/malgamves/CommunityWriterPrograms/blob/master/README.md" rel="noopener noreferrer"&gt;CommunityWriterPrograms&lt;/a&gt; repo&lt;/strong&gt; was my 2021 breakthrough. I found companies like Sanity, Strapi, ContentLabs, LogRocket. Currently working with Draft.dev as my stable client. This repo really helped change my financial situation. Another resource is &lt;a href="https://www.notion.so/Automating-Notion-Image-Handling-with-Make-com-and-Dropbox-A-Step-by-Step-Guide-6aae4d920d7d4836904bb0090fe73941?pvs=21" rel="noopener noreferrer"&gt;who pays technical writers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cold outreach to startups&lt;/strong&gt; requires mastering the &lt;a href="https://woodpecker.co/blog/how-to-write-a-cold-email-that-actually-works-six-step-tutorial/" rel="noopener noreferrer"&gt;art of cold DMs&lt;/a&gt;. Right now is the perfect timing because AI startups are launching daily. Find something genuinely interesting about their product, then pitch. Don't spam, be specific.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your network, including ghost writing,&lt;/strong&gt; is more powerful than you think. Here's the reality: 60% of my work isn't in my name. From 2020-2023, it was 90% ghostwritten. Rates were $150-300 per piece. This has pros and cons. You get paid, but don't build a public portfolio. Weigh it based on your current needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dev advocate connections&lt;/strong&gt; often need content support and pay well. Build relationships with people in these roles.&lt;/p&gt;

&lt;p&gt;The CommunityWriterPrograms repo update I'm working on is me giving back to the resource that really helped fund my career transition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Content That Opens Doors
&lt;/h2&gt;

&lt;p&gt;First, cultivate a &lt;a href="https://fs.blog/carol-dweck-mindset/" rel="noopener noreferrer"&gt;growth mindset&lt;/a&gt;. Be ready to try things, experiment, fail, and iterate.&lt;/p&gt;

&lt;p&gt;Show up consistently and solve real problems. Thanks to AI tools like Claude and ChatGPT, anyone can build anything now. But you need to build with product expertise and create buzz around solutions people need.&lt;/p&gt;

&lt;p&gt;Let me give you a concrete example: The biggest issue in coding with LLMs today? Leaked secrets. Everyone's bashing new coders for exposing API keys, but nobody's helping them. So I built &lt;a href="https://www.npmjs.com/package/secure-commit" rel="noopener noreferrer"&gt;npx secure-commit scan&lt;/a&gt; and shared about it. That's the kind of thing that opens opportunities.&lt;/p&gt;

&lt;p&gt;Be part of the conversation. Join community discussions on Discord, Reddit, and X. Make open source contributions by writing articles for tools that don't have any and tag the maintainers. Participate in discussions around topics you care about. Don't just consume, create.&lt;/p&gt;

&lt;p&gt;Apply to speak at events, and volunteer to help. Being a helper is underrated. You never know who's watching.&lt;/p&gt;

&lt;p&gt;Stay curious and locked in. The &lt;a href="https://github.com/Umoren/100-nigerian-dev-articles" rel="noopener noreferrer"&gt;100 Nigerian Dev Articles&lt;/a&gt; project I'm running is an example of how I stay curious about our ecosystem and document what I find.&lt;/p&gt;

&lt;p&gt;The pattern that's worked for me: &lt;/p&gt;

&lt;p&gt;Find problems → Build solutions → Document the journey → Share with community → Opportunities follow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unconventional Ways to Monetize Your Skills
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"Interning" for established technical writers or dev advocates&lt;/strong&gt; might not be for everyone, but it's one of the best learning experiences. You don't just learn how to write; you learn client management, project scoping, and communication patterns.&lt;/p&gt;

&lt;p&gt;What I learned from this experience is important: don't get exploited, but working under someone who can afford to delegate teaches you the business side. I learned more about client relationships from ghost writing than I ever would have figured out alone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create text-based educational content with micro-monetization.&lt;/strong&gt; Build something like a practical course or guide, add a Buy Me a Coffee button, or request GitHub sponsorship. If it delivers real value, people will support you.&lt;/p&gt;

&lt;p&gt;Document your learning journey with a specific technology. People pay for organized, tested knowledge that saves them time.&lt;/p&gt;

&lt;p&gt;Don't give up if it doesn't work right away. Take it as experience because you're growing either way. Each "failed" project teaches you about audience, packaging, and delivery.&lt;/p&gt;

&lt;p&gt;You're building multiple income streams while developing business skills that most freelancers never learn.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hard Truths About Freelance Challenges
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Inconsistent income&lt;/strong&gt; is the biggest challenge. Writing programs don't last forever. They can suspend anytime. Agencies are more predictable but tougher to break into.&lt;/p&gt;

&lt;p&gt;Solution: Apply to as many writing programs as possible. Programs, agencies, cold DMs. But when you get them, treat each like a full-time job and give your best work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time management and communication&lt;/strong&gt; is where I've failed a lot. Managing time and communicating ETAs properly. I've only been saved by the quality of my work, but don't rely on that. Be professional and reliable as much as possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Payment bottlenecks&lt;/strong&gt; still hurt me. Inconsistent payment schedules, gateway issues. Try &lt;a href="https://www.mainstack.co/" rel="noopener noreferrer"&gt;Mainstack&lt;/a&gt; if you're a Nigerian freelancer dealing with international payments. It's been a great solution I found recently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isolation kills productivity.&lt;/strong&gt; Don't be a lone wolf. Have a healthy community of friends who understand what you're doing and can help you navigate challenges.&lt;/p&gt;

&lt;p&gt;What's worked for me is treating inconsistency as the norm, not the exception. Build multiple streams, communicate like a professional, solve your payment infrastructure early, and lean on community when things get tough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Products from Your Knowledge
&lt;/h2&gt;

&lt;p&gt;Start with what you already know deeply. My AI transition journey could be a course called "From Technical Writer to AI Developer." Don't create in a vacuum.&lt;/p&gt;

&lt;p&gt;The first thing I recommend is asking your audience what they're struggling with. The 100 Nigerian articles project is part of market research. I'm seeing what resonates. For example, when I highlighted Linda Ikechukwu's article on &lt;a href="https://x.com/saameeey/status/1933257976581697913" rel="noopener noreferrer"&gt;creating a technical writer resume&lt;/a&gt;, it got the most engagement of any article I shared in the collection. That tells me people are more interested in career-growth related stories.&lt;/p&gt;

&lt;p&gt;Validate before you build. Share your course outline, see what gets engagement. Build the landing page before the course.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Technical Writing is Evolving
&lt;/h2&gt;

&lt;p&gt;AI is changing everything. I lived this. I went from documenting other people's code to building AI systems myself.&lt;/p&gt;

&lt;p&gt;The future belongs to hybrid roles: Writer + Developer, Writer + AI specialist. Companies want people who understand what they're documenting.&lt;/p&gt;

&lt;p&gt;The skills I think are most important now are learning the technologies you write about, AI tool integration (obvious but true), developer experience thinking, and product sense, not just writing sense.&lt;/p&gt;

&lt;p&gt;My career is proof: the writers who evolve beyond just writing will have the most opportunities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Assets That Work for You
&lt;/h2&gt;

&lt;p&gt;The thing that connects everything in my journey is this: build assets, serve community, stay curious. That's what's helped me future-proof my career, even when I didn't know what I was doing.&lt;/p&gt;

&lt;p&gt;Whether it's the CommunityWriterPrograms repo that funded my transition, the AI projects that got me hired, or the 100 Nigerian articles project building my speaking credibility, each asset has compounded over time.&lt;/p&gt;

&lt;p&gt;I learned to focus less on chasing individual clients and more on building things that bring opportunities to me. It takes longer upfront, but the payoff is more sustainable than constantly hustling for the next gig.&lt;/p&gt;

&lt;p&gt;The technical writing industry needs people who understand technology, not just grammar. I'm still figuring this out, but positioning yourself as someone who can bridge that gap seems to be working.&lt;/p&gt;

</description>
      <category>devrel</category>
      <category>programming</category>
      <category>ai</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Optimize Your Node.js API with Clustering, Load Testing, and Advanced Caching</title>
      <dc:creator>Samuel Umoren</dc:creator>
      <pubDate>Thu, 19 Oct 2023 15:18:20 +0000</pubDate>
      <link>https://dev.to/umoren/optimize-your-nodejs-image-recognition-api-with-clustering-load-testing-and-advanced-caching-1pj5</link>
      <guid>https://dev.to/umoren/optimize-your-nodejs-image-recognition-api-with-clustering-load-testing-and-advanced-caching-1pj5</guid>
      <description>&lt;p&gt;Imagine your Node.js application struggling to keep up as user requests pile up, creating a performance bottleneck that no developer wants to face. This is a common challenge in single-threaded Node.js applications. But fear not; robust solutions exist to break free from these limitations. In this article, we'll delve into three powerful strategies to supercharge your Node.js API: Node.js clustering to leverage multiple CPU cores, load testing with Autocannon to measure performance, and advanced optimizations like Least Recently Used (LRU) caching and logging.&lt;/p&gt;

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

&lt;p&gt;To follow along with this article, you’ll need to have the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js and npm installed&lt;/li&gt;
&lt;li&gt;Basic understanding of TypeScript and Express.js&lt;/li&gt;
&lt;li&gt;Clone the starter repository and switch to the starter branch: &lt;a href="https://github.com/Umoren/clustering-demo" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Understanding Node.js Clustering
&lt;/h2&gt;

&lt;p&gt;Think of Node.js clustering as a team of workers in a factory assembly line. In a single-threaded environment, just one worker (or CPU core) does all the tasks. This lone worker can get overwhelmed, leading to performance bottlenecks. Imagine multiplying that worker to fully utilize all the available assembly lines (or CPU cores). That's what Node.js clustering does—it distributes incoming network requests across multiple CPU cores, making your application more scalable and efficient.&lt;/p&gt;

&lt;p&gt;The importance of Node.js clustering can't be overstated. It's like upgrading from a one-man shop to a full-fledged factory. Distributing tasks speeds up the production line and makes it more resilient. If one worker fails, others can pick up the slack, ensuring your API remains responsive even under heavy load.&lt;/p&gt;

&lt;p&gt;Let's examine how clustering is implemented in your cloned starter branch &lt;code&gt;app.ts&lt;/code&gt;. The relevant part is as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cluster&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;cluster&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;os&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;os&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;numCPUs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cpus&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isMaster&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Master &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is running`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Fork workers&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;numCPUs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fork&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exit&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="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Worker &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; died`&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;In this snippet, we first import the &lt;code&gt;cluster&lt;/code&gt; and &lt;code&gt;os&lt;/code&gt; modules. We then determine the number of CPU cores available on the machine using &lt;code&gt;os.cpus().length&lt;/code&gt;. If the code runs on the master process, it forks a worker for each CPU core. This way, we can fully utilize the multi-core capabilities of modern CPUs to improve the performance of our Node.js application.&lt;/p&gt;

&lt;p&gt;When you run the project with &lt;code&gt;npm run dev&lt;/code&gt;, you should see this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FEyiB5Uf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FEyiB5Uf.png" alt="Terminal showing the app server logs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These logs confirm that your application is running in a clustered mode, utilizing all available CPU cores. Each worker is equipped with an instance of the TensorFlow model, ready to handle incoming requests. Let’s break it down:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Master is Running&lt;/strong&gt;: The &lt;code&gt;[INFO] Master 23249 is running&lt;/code&gt; message signifies that the master process has successfully started. The number &lt;code&gt;23249&lt;/code&gt; is the process ID of the master.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workers are Running&lt;/strong&gt;: Messages like &lt;code&gt;[INFO] Worker 23254 running on http://localhost:3000&lt;/code&gt; indicate that worker processes have been forked and are running. Each worker listens on the same port (&lt;code&gt;3000&lt;/code&gt; in this case). The numbers &lt;code&gt;23254&lt;/code&gt;, &lt;code&gt;23255&lt;/code&gt;, etc., are the process IDs of the workers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model Loaded&lt;/strong&gt;: The &lt;code&gt;[INFO] Model loaded&lt;/code&gt; messages confirm that the TensorFlow model has been successfully loaded into each worker. This is crucial because each worker will handle incoming requests independently.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Load Testing with Autocannon
&lt;/h2&gt;

&lt;p&gt;Imagine your API as a busy highway. You've added more lanes (workers) to handle traffic, but how do you know it's enough? Well, that’s where you need to load-test your API. To do that, we’ll use Autocannon. Autocannon is a fast HTTP/1.1 benchmarking tool powered by Node.js. It's like a traffic simulator for your API, allowing you to understand how your application performs under different load levels. &lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up Autocannon
&lt;/h3&gt;

&lt;p&gt;First, you'll need to install Autocannon. You can do this by running &lt;code&gt;npm install autocannon&lt;/code&gt; in your terminal. Once installed, you can create an &lt;code&gt;autocannon-test.js&lt;/code&gt;** and add these lines of code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;autocannon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;autocannon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;boundary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;----WebKitFormBoundary7MA4YWxkTrZu0gW&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/Users/mac/Desktop/sammy23/image-recognition-api/image1.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`--&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;boundary&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\r\n`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Disposition: form-data; name="image"; filename="image1.png"&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type: image/png&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;fileContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`\r\n--&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;boundary&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;--\r\n`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;autocannon&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000/upload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&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="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`multipart/form-data; boundary=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;boundary&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="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error running autocannon:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test completed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;autocannon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;track&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;renderProgressBar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;In the &lt;code&gt;autocannon-test.js&lt;/code&gt; file, you'll see several key components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;File and Boundary Setup&lt;/strong&gt;: The &lt;code&gt;boundary&lt;/code&gt; and &lt;code&gt;filePath&lt;/code&gt; variables define the multipart form boundary and the path to the image file you'll upload. The &lt;code&gt;fileContent&lt;/code&gt; reads the image file into a buffer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Request Configuration&lt;/strong&gt;: The &lt;code&gt;instance&lt;/code&gt; object specifies the test parameters. The &lt;code&gt;url&lt;/code&gt; is where the API is running, &lt;code&gt;method&lt;/code&gt; is POST, and &lt;code&gt;connections&lt;/code&gt; and &lt;code&gt;duration&lt;/code&gt; define the load you want to simulate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Headers and Body&lt;/strong&gt;: The &lt;code&gt;headers&lt;/code&gt; and &lt;code&gt;body&lt;/code&gt; fields in the &lt;code&gt;instance&lt;/code&gt; object specify the HTTP headers and the request's body. The body is a buffer that includes the image file and the multipart form boundary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Running and Tracking&lt;/strong&gt;: The &lt;code&gt;autocannon()&lt;/code&gt; function runs the test and &lt;code&gt;autocannon.track(instance, { renderProgressBar: true })&lt;/code&gt; tracks the progress.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After running &lt;code&gt;node autocannon-test.js&lt;/code&gt;, you'll see a comprehensive output showing your API's performance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FrbMbul1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FrbMbul1.png" alt="Terminal showing autocannon test results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The average latency is 1526.9 ms, and the API can manage an average of 6.44 requests per second. The "2xx" count of 386 confirms that most requests were successful, indicating a stable API under load.&lt;/p&gt;

&lt;p&gt;The latency and throughput metrics offer a nuanced view of your API's behavior. For instance, 50% of the requests have a latency less than or equal to 1450 ms, and 99% are under 3765 ms. These figures help you identify bottlenecks and make informed decisions for further optimizations, ensuring that your API can handle real-world usage patterns effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Caching with LRU Cache
&lt;/h2&gt;

&lt;p&gt;To further improve the performance of the Image Recognition API, we’ll add some advanced optimization techniques. One of those techniques is the Least Recently Used (LRU) Caching. LRU caching keeps recently used data closer (in memory), so when the same data is requested again, the system doesn't have to go through the lengthy process of fetching it from the source.&lt;/p&gt;

&lt;p&gt;For this sample codebase, we’ll use a  basic implementation of LRU caching:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// The maximum number of items to store in the cache&lt;/span&gt;
    &lt;span class="na"&gt;maxAge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="c1"&gt;// Items expire after 30 minutes&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;classificationCache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LRUCache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ... (other parts of the code)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cachedResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;classificationCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;predictions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cachedResult&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ... (other parts of the code)&lt;/span&gt;

&lt;span class="nx"&gt;classificationCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;predictions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In the code above, we initialize an LRU cache with a maximum of 500 items and a 30-minute expiration time for each item. When an image is uploaded for classification, the API first checks if the result is already in the cache (&lt;code&gt;classificationCache.get(imageId)&lt;/code&gt;). If it is, the cached result is returned, saving computational time. If not, the image is processed, and the result is stored in the cache (&lt;code&gt;classificationCache.set(imageId, predictions)&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The LRU cache is a performance booster for this image recognition API, particularly useful when dealing with repetitive image classifications. Imagine a scenario where the same images are uploaded and classified multiple times. Without caching, the API would redundantly perform the same computationally expensive TensorFlow operations for each request, leading to higher latency and increased CPU load.&lt;/p&gt;

&lt;p&gt;By implementing LRU caching, we store the classification results of recently processed images. When the API receives a request to classify an image it has seen recently, it can simply retrieve the result from the cache instead of re-running the classification. This significantly reduces the API's response time and lightens the load on the CPU, making the system more efficient and scalable.&lt;/p&gt;

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

&lt;p&gt;In large Node.js applications, clustering is an indispensable tool for harnessing the full power of your machine's CPU cores. It's not just about making your application faster; it's about making it resilient and capable of handling an increasing number of requests without choking. By distributing the load across multiple worker processes, you can ensure your application remains responsive even under heavy stress.&lt;/p&gt;

&lt;p&gt;This article explored the essential techniques for optimizing a Node.js API. We delved into clustering to improve performance, used Autocannon for load testing, and implemented LRU caching to reduce redundant operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;For those eager to dive deeper into the topics covered, here are some additional resources that can further your understanding and skills:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Node.js Clustering&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/api/cluster.html" rel="noopener noreferrer"&gt;Node.js Documentation on Cluster Module&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@tpreusse/scaling-node-js-applications-8492bd8afadc" rel="noopener noreferrer"&gt;Scaling Node.js Applications&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load Testing with Autocannon&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/mcollina/autocannon" rel="noopener noreferrer"&gt;Autocannon GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://k6.io/docs/using-k6/metrics" rel="noopener noreferrer"&gt;Understanding Load Testing Metrics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LRU Caching&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.interviewcake.com/concept/java/lru-cache" rel="noopener noreferrer"&gt;LRU Cache Explained&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.geeksforgeeks.org/lru-cache-implementation/" rel="noopener noreferrer"&gt;LRU Cache Implementation in JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Logging and Monitoring&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/winstonjs/winston" rel="noopener noreferrer"&gt;Winston: A Logging Library for Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.dynatrace.com/news/blog/easy-monitoring-of-node-js-applications/" rel="noopener noreferrer"&gt;Monitoring Node.js Applications&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TensorFlow.js and MobileNet&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.tensorflow.org/js" rel="noopener noreferrer"&gt;TensorFlow.js Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ai.googleblog.com/2017/06/mobilenets-open-source-models-for.html" rel="noopener noreferrer"&gt;MobileNet: Efficient On-Device Vision Applications&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>cache</category>
      <category>programming</category>
    </item>
    <item>
      <title>Automating Code Review in Node.js with DeepSource</title>
      <dc:creator>Samuel Umoren</dc:creator>
      <pubDate>Sun, 08 Oct 2023 11:55:40 +0000</pubDate>
      <link>https://dev.to/umoren/automating-code-review-in-nodejs-with-deepsource-3fh7</link>
      <guid>https://dev.to/umoren/automating-code-review-in-nodejs-with-deepsource-3fh7</guid>
      <description>&lt;p&gt;In the fast-paced world of Node.js REST API development, maintaining high code quality often becomes a challenging task. Manual code reviews, although essential, can be time-consuming and might not catch every issue. This is where automated code review tools like &lt;strong&gt;DeepSource&lt;/strong&gt; come into play.&lt;/p&gt;

&lt;p&gt;DeepSource not only automates the code review process but also &lt;strong&gt;integrates&lt;/strong&gt; seamlessly with your development workflow. In this article, we'll walk you through setting up DeepSource for a &lt;strong&gt;Node.js REST API project&lt;/strong&gt;, specifically focusing on a fintech API. We'll cover everything from connecting your project to a version control system to configuring DeepSource to run &lt;strong&gt;automated code reviews&lt;/strong&gt; and setting up alerts for new issues.&lt;/p&gt;

&lt;p&gt;By the end of this guide, you'll have a &lt;strong&gt;streamlined code review process&lt;/strong&gt; that elevates the quality of your codebase, making it more maintainable, robust, and secure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;Before diving into the step-by-step guide, make sure you have the following prerequisites in place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js and npm&lt;/strong&gt;: Ensure you've installed Node.js and npm on your system. You can download them from &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeepSource Account&lt;/strong&gt;: Sign up for a DeepSource account &lt;a href="https://deepsource.io/signup/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. You'll use this to run automated code reviews.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;To get started, clone the repo of the Fintech API project from GitHub, install the dependencies, and switch to the starter branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;-b&lt;/span&gt; starter https://github.com/Umoren/fintech-api.git
&lt;span class="nb"&gt;cd &lt;/span&gt;fintech-api
npm &lt;span class="nb"&gt;install
&lt;/span&gt;git checkout starter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When this runs, your API will be running on &lt;a href="http://locahost:5000/api" rel="noopener noreferrer"&gt;http://localhost:5000/api&lt;/a&gt;. You can explore the API endpoints from the documentation at &lt;a href="http://localhost:5000/api/docs" rel="noopener noreferrer"&gt;http://localhost:5000/api/docs&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  DeepSource Integration
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Head over to DeepSource and sign in with your GitHub account.&lt;/li&gt;
&lt;li&gt;Activate DeepSource for your cloned repository.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fy2SczR0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fy2SczR0.png" alt="Activate Deepsource for your repo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;code&gt;deepsource.toml&lt;/code&gt; file in your project and add these lines of code to set the JavaScript analyzer:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="nn"&gt;[[analyzers]]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"javascript"&lt;/span&gt;

  &lt;span class="nn"&gt;[analyzers.meta]&lt;/span&gt;
  &lt;span class="py"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"nodejs"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;When this is done, commit and push the code.&lt;/p&gt;

&lt;p&gt;DeepSource will automatically analyze your code on every push, helping you maintain high code quality.&lt;/p&gt;

&lt;p&gt;With these steps, you've set up a Node.js REST API project and integrated it with DeepSource for automated code reviews. Next, we'll delve into how to make the most out of DeepSource's features to streamline your development workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Prioritizing and Reviewing Issues with DeepSource&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;After integrating DeepSource, the tool scans your entire codebase and flags various issues that could range from code smells and anti-patterns to outright security vulnerabilities. One of the first things you'll notice is how DeepSource categorizes these issues based on their severity. This allows you to focus on the most critical issues first, ensuring that your codebase remains robust and secure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FixZ1Yhf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FixZ1Yhf.png" alt="Reviewing issues"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DeepSource also offers the functionality to group similar issues. This feature is particularly useful when dealing with large codebases where similar issues might be scattered across different files. It streamlines the process of tackling these issues in batches rather than one at a time.&lt;/p&gt;

&lt;p&gt;In a team setting, DeepSource allows for the assignment of issues to specific team members. This not only ensures accountability but also speeds up the review process, making it efficient and effective.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating Issue Creation for Immediate Action
&lt;/h2&gt;

&lt;p&gt;One of the standout features of DeepSource is its ability to automate the issue creation process. This is particularly useful for teams that want to immediately act upon the issues flagged during the code review. By automating this process, you ensure that each issue receives the attention it deserves without manual intervention.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Understanding Severity Categories in DeepSource&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;DeepSource categorizes issues into four main severity levels: Critical, High, Medium, and Low. These categories help teams prioritize which issues need immediate attention and which can be scheduled for later.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Critical Severity&lt;/strong&gt;: These are issues that pose a direct threat to the application's security or functionality. They require immediate attention and should be resolved as soon as possible to prevent potential breaches or system failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High Severity&lt;/strong&gt;: While not immediately dangerous, high-severity issues can have a significant impact on code quality and performance. These should be addressed promptly to maintain a robust codebase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Medium Severity&lt;/strong&gt;: These issues are less urgent but still important for long-term code quality and maintainability. They often include things like code smells or minor performance bottlenecks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low Severity&lt;/strong&gt;: These are mostly cosmetic issues that don't impact the functionality or security of the application. However, resolving them can improve code readability and maintainability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s illustrate the automating issue creation process by working on the “Detected usage of &lt;code&gt;any&lt;/code&gt; type” issue. When you click on that issue, you’ll see this screen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FB1N3ycr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FB1N3ycr.png" alt="Detected issues screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a critical issue. Click on the &lt;em&gt;Create this issue on GitHub&lt;/em&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FNKkig7r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FNKkig7r.png" alt="Creating issues on Github"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Automating issue creation is an excellent way for you to ensure that critical issues are promptly addressed, thereby maintaining the codebase's integrity and quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating DeepSource into Your Node.js REST API Development Workflow
&lt;/h2&gt;

&lt;p&gt;DeepSource offers a &lt;a href="https://docs.deepsource.com/docs/analyzers-test-coverage" rel="noopener noreferrer"&gt;Test Coverage analyzer&lt;/a&gt; that can be easily integrated into your Node.js REST API project. To set this up, you'll need to update the &lt;code&gt;deepsource.toml&lt;/code&gt; configuration file to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="s"&gt;version = &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;

  &lt;span class="pi"&gt;[[&lt;/span&gt;&lt;span class="nv"&gt;analyzers&lt;/span&gt;&lt;span class="pi"&gt;]]&lt;/span&gt;
  &lt;span class="s"&gt;name = "javascript"&lt;/span&gt;

    &lt;span class="s"&gt;[analyzers.meta]&lt;/span&gt;
    &lt;span class="s"&gt;environment = ["nodejs"]&lt;/span&gt;

  &lt;span class="s"&gt;[[analyzers]]&lt;/span&gt;
  &lt;span class="s"&gt;name = "test-coverage"&lt;/span&gt;
  &lt;span class="s"&gt;enabled = &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting Up Code Coverage with DeepSource
&lt;/h3&gt;

&lt;p&gt;DeepSource only supports &lt;code&gt;cobertura&lt;/code&gt; for code coverage reporting. To set this up in our project, update the scripts field of your &lt;code&gt;package.json&lt;/code&gt; with this:&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;"coverage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nyc --reporter=cobertura npm test"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run code coverage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run coverage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a &lt;code&gt;coverage/cobertura-coverage.xml&lt;/code&gt; file at the root of your project.&lt;/p&gt;

&lt;p&gt;Navigate to your Deepsource dashboard, head to Settings and copy your repo DSN&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2F0HNuvuF.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2F0HNuvuF.png" alt="Data Source Name"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the DEEPSOURCE_DSN env variable as a system variable&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;export &lt;/span&gt;&lt;span class="nv"&gt;DEEPSOURCE_DSN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;DSN URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, install the DeepSource CLI using this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://deepsource.io/cli | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the report coverage command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./bin/deepsource report &lt;span class="nt"&gt;--analyzer&lt;/span&gt; test-coverage &lt;span class="nt"&gt;--key&lt;/span&gt; javascript &lt;span class="nt"&gt;--value-file&lt;/span&gt; ./coverage/cobertura-coverage.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it runs successfully, you should see this on your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜  fintech-api git:&lt;span class="o"&gt;(&lt;/span&gt;master&lt;span class="o"&gt;)&lt;/span&gt; ✗ ./bin/deepsource report &lt;span class="nt"&gt;--analyzer&lt;/span&gt; test-coverage &lt;span class="nt"&gt;--key&lt;/span&gt; javascript &lt;span class="nt"&gt;--value-file&lt;/span&gt; ./coverage/cobertura-coverage.xml
DeepSource | Artifact published successfully

Analyzer  test-coverage
Key       javascript
Message   Artifact successfully uploaded &lt;span class="k"&gt;for &lt;/span&gt;repository fintech-api on commit 330b1f913a8ec2ff0a81594d1eacacdf01d4b3e1.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the Metrics tab on your DeepSource dashboard and you should see coverage report graphs generated like this:&lt;/p&gt;

&lt;p&gt;Condition Coverage: This metric is particularly useful for understanding how well your code handles different conditional logic paths.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FbJ3BPUi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FbJ3BPUi.png" alt="Condition Coverage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Composite Coverage: This provides a more holistic view of how well your code is tested.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fh9Cecsx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fh9Cecsx.png" alt="Composite Coverage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Branch Coverage: This measures the code coverage, including the various branches in your code like &lt;strong&gt;&lt;code&gt;if&lt;/code&gt;&lt;/strong&gt;, &lt;strong&gt;&lt;code&gt;else&lt;/code&gt;&lt;/strong&gt;, and &lt;strong&gt;&lt;code&gt;switch&lt;/code&gt;&lt;/strong&gt; statements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FvNo4yIK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FvNo4yIK.png" alt="Branch Coverage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Line Coverage: This is the code coverage excluding the branches. It simply tells you what percentage of your lines of code are executed during tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FByk2hOt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FByk2hOt.png" alt="Line Coverage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Documentation Coverage: This unique metric tracks the extent to which your application code is documented. It's a good indicator of code maintainability.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fd8aqOvZ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2Fd8aqOvZ.png" alt="Documentation Coverage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;External Dependencies: This shows the total number of 3rd-party dependencies used in your repository. It is a useful metric for understanding the complexity and potential security risks in your project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FOW5Sn3z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FOW5Sn3z.png" alt="External Dependencies"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring CI/CD Pipeline with GitHub Actions
&lt;/h2&gt;

&lt;p&gt;Let’s set up a CI/CD pipeline with GitHub Actions so that whenever you make a push action, a coverage report will be generated. Create a &lt;code&gt;.github/workflows/deepsource-analysis.yml&lt;/code&gt; file and add these lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DeepSource and Test Coverage&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deepsource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;DEEPSOURCE_DSN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DEEPSOURCE_DSN }}&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout Code&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
      &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v2&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;16'&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Dependencies&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;

    &lt;span class="c1"&gt;# Run your tests and generate coverage file here&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Tests and Generate Coverage&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run coverage&lt;/span&gt;

    &lt;span class="c1"&gt;# Report test-coverage to DeepSource using CLI&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Report test-coverage to DeepSource&lt;/span&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;# Install the CLI&lt;/span&gt;
        &lt;span class="s"&gt;curl https://deepsource.io/cli | sh&lt;/span&gt;

        &lt;span class="s"&gt;# Send the report to DeepSource&lt;/span&gt;
        &lt;span class="s"&gt;./bin/deepsource report --analyzer test-coverage --key javascript --value-file ./coverage/cobertura-coverage.xml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure you add your DSN to the GitHub Repository secrets. &lt;/p&gt;

&lt;p&gt;This workflow will run DeepSource analysis and report back the results. If any issues are found, they can be automatically converted into GitHub issues using the methods described in the previous section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FWZksGPP.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FWZksGPP.png" alt="Github Action for test coverage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Github Action for test coverage&lt;/p&gt;

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

&lt;p&gt;Automating code reviews and integrating quality checks into your development workflow are not just best practices; they're necessities for scaling software projects efficiently. DeepSource serves as an invaluable tool for this, offering a comprehensive suite of metrics that go beyond simple lint checks. From identifying anti-patterns to calculating various code coverage metrics, DeepSource provides a 360-degree view of your code quality.&lt;/p&gt;

&lt;p&gt;In this article, we've walked through the process of setting up DeepSource for a Node.js REST API project in the fintech space. We've demonstrated how to automate the creation of GitHub issues based on code analysis, and how to integrate DeepSource into your CI/CD pipeline using GitHub Actions. The aim is to make the code review process as frictionless as possible, allowing you to focus more on feature development and less on code maintenance.&lt;/p&gt;

&lt;p&gt;By adopting tools like DeepSource and incorporating them into your development workflow, you're not just writing better code; you're creating a more sustainable, manageable, and scalable software architecture. And that's a win for everyone involved—developers, managers, and stakeholders alike.&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>node</category>
      <category>deepsource</category>
    </item>
    <item>
      <title>Improve Data Fetching in Next.js Applications through SWR</title>
      <dc:creator>Samuel Umoren</dc:creator>
      <pubDate>Fri, 19 May 2023 19:50:04 +0000</pubDate>
      <link>https://dev.to/umoren/improve-data-fetching-in-nextjs-applications-through-swr-8od</link>
      <guid>https://dev.to/umoren/improve-data-fetching-in-nextjs-applications-through-swr-8od</guid>
      <description>&lt;p&gt;This article was originally published  &lt;a href="https://soshace.com/using-swr-for-efficient-data-fetching-in-next-js-applications/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fetching and rendering data from APIs is one of the core essentials of front-end development. The basic way of fetching data in JavaScript is to use local &lt;code&gt;fetch&lt;/code&gt; or a third-party library like &lt;code&gt;axios&lt;/code&gt;, input the right HTTP method for the request, including headers, parse the result in JSON, and render it to the DOM. This wouldn’t work well with modern web applications because of their architectures' complexity. Users will only use a web app that’s fast, reliable, and responsive; that means if they request a resource, they want it delivered in &amp;lt;3s. As a result, developers need to use tools or libraries that improve the data-fetching experience in their applications.&lt;/p&gt;

&lt;p&gt;In React, data fetching is a side effect, and it provides the &lt;code&gt;useEffect&lt;/code&gt; Hook for performing this side effect. Data fetching in React typically would look like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com/data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;fetchData&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="c1"&gt;// The empty array `[]` ensures the effect only runs once on mount and unmount.&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;However, when building server-rendered applications, Nextjs is preferred. Data fetching in Next.js can be done using different methods, depending on the desired rendering strategy, that is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When you want to fetch data at build time and generate static HTML pages, Nextjs provides &lt;code&gt;getStaticProps&lt;/code&gt; for that.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When you have dynamic routes and want to generate static HTML pages for each route, use &lt;code&gt;getStaticPaths&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When you need to fetch data on each request, providing a server-rendered experience, use &lt;code&gt;getServerSideProps&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can still use client-side data fetching when you don't need to pre-render the data or when you want to fetch data that depends on user interactions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is common to see Next.js applications that make use of client-side data fetching. The challenge with this technique of data fetching is that you have to render data based on user interaction, which can lead to several issues if not handled properly.&lt;/p&gt;

&lt;p&gt;This is why Vercel created SWR (stale-while-revalidate). Without a solution like SWR, you’re likely to face difficulties managing caching, ensuring data synchronization, handling errors, and providing real-time updates. Additionally, handling loading states can become cumbersome, and you might end up with a lot of boilerplate code just for fetching, caching, and managing the data as the codebase grows. SWR addresses these challenges by providing built-in caching, automatic cache revalidation, error retries, support for real-time updates, and a Hooks-based API that simplifies data management.&lt;/p&gt;

&lt;p&gt;In this article, I’ll introduce you to how SWR works, its key concepts, and how to use it for efficient data fetching in client-side Next.js applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  How SWR works
&lt;/h2&gt;

&lt;p&gt;To understand how SWR works, you need to be conversant with these key concepts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Caching: Caching is like storing food in the fridge. It's a way to save data so it can be quickly accessed later without needing to fetch it from the source (server) every time. This speeds up the process of retrieving data and reduces the load on the server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Revalidation: Revalidation is like checking if the food in the fridge is still good or needs replacing with a fresh meal. In the context of data fetching, revalidation means checking if the cached data is still valid or if it needs to be updated with new data from the server. With SWR, this process happens automatically in the background, ensuring your data is always up-to-date.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stale-While-Revalidate: Imagine you have a fridge with food inside. When you're hungry, you grab something from the fridge (cached data). At the same time, a friend starts cooking a fresh meal (fetching new data). You eat the food from the fridge while waiting for the fresh meal to be ready. Once done, the fridge is restocked with fresh food (cache revalidation).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;SWR is a data fetching library that implements the Stale-While-Revalidate (SWR) strategy. It fetches, caches, and revalidates data in the background to provide an efficient and seamless user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we’ll be building
&lt;/h2&gt;

&lt;p&gt;To appreciate SWR, you need to build something with it. In this tutorial, we’ll build a product store with Nextjs. While building this demo app, you’ll get to learn the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Fetching data using the &lt;code&gt;useSWR&lt;/code&gt; hook&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handling Errors and Loading States&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implementing optimistic UI updates with SWR&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implementing infinite scrolling in Next.js with SWR&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pre-requisites
&lt;/h3&gt;

&lt;p&gt;You’ll need the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Nodejs ≥v16&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Code editor (preferably VS code)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Code terminal&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Package manager (yarn preferably)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Knowledge of JavaScript and Reactjs&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The complete code is on &lt;a href="https://github.com/Umoren/product-store-swr" rel="noopener noreferrer"&gt;Github&lt;/a&gt;, and the demo app is &lt;a href="https://product-store-swr.vercel.app/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Run this command on your terminal to create a nextjs project.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npx create-next-app product-store&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;product-store&lt;span class="p"&gt;;&lt;/span&gt; code &lt;span class="nb"&gt;.&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;The product store will have a &lt;code&gt;/product&lt;/code&gt; parent route with these sub-routes: &lt;code&gt;/product/listing&lt;/code&gt; and &lt;code&gt;/product/detail&lt;/code&gt;. Run this command from the root directory on your terminal to create these page routes.&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;pages&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;product&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;product&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;.js upload.js


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

&lt;/div&gt;

&lt;p&gt;Go back to the root directory and create these UI components&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;mkdir &lt;/span&gt;components&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;components&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;touch &lt;/span&gt;ProductList.jsx ProductCard.jsx ErrorMessage.jsx LoadingIndicator.jsx ProductUploadForm.jsx


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

&lt;/div&gt;

&lt;p&gt;Navigate back to the root director and install these packages:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

yarn add swr tailwindcss postcss autoprefixer react-infinite-scroll-component&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;See how to configure tailwindcss &lt;a href="https://tailwindcss.com/docs/guides/nextjs" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fetching, Displaying, and Updating Data with SWR
&lt;/h3&gt;

&lt;p&gt;Firstly, let’s create a custom React hook that’ll fetch data from the Products fakeapi endpoint using SWR. Add these lines of code to &lt;code&gt;hooks/useFetch.js&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useSWR&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;swr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;An error occurred while fetching the data.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useFetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSWR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://fakestoreapi.com/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;path&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="nx"&gt;fetcher&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;error&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;useFetch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Here, we define a custom React hook, &lt;code&gt;useFetch&lt;/code&gt;, which leverages the &lt;code&gt;useSWR&lt;/code&gt; hook for data fetching. It takes a &lt;code&gt;path&lt;/code&gt; as input and constructs a full &lt;code&gt;URL&lt;/code&gt; to fetch data from the Fake Store API. The fetcher function handles making the request and error checking. &lt;code&gt;useFetch&lt;/code&gt; returns an object with the fetched data, potential errors, and a loading state.&lt;/p&gt;

&lt;p&gt;Now let’s display the product list.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;code&gt;components/ProductCard&lt;/code&gt; and add these lines of code.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Link&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;next/link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Image&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;next/image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ProductCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-white rounded-lg shadow-md p-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;
                &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"responsive"&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-lg font-semibold mb-2"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-gray-600 mb-2"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-between items-center"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-lg font-bold"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;$&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;
                    &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`/product/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                    &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-blue-600 hover:text-blue-800"&lt;/span&gt;
                &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                    View Details
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ProductCard&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This ProductCard component renders a single product card. The card includes an image, product title, category, price, and a link to the product details page. The image is set using Next.js' Image component, which automatically optimizes the image for performance. The link to the product details page is wrapped in Next.js' Link component for client-side navigation.&lt;/p&gt;

&lt;p&gt;Navigate to &lt;code&gt;component/ProductList.jsx&lt;/code&gt; and add these lines of code.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useFetch&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;./hooks/useFetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ProductCard&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;./ProductCard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ProductList&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLoading&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Error: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductCard&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;product&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ProductList&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Here, we define the &lt;code&gt;ProductList&lt;/code&gt; component, which fetches and displays a list of products. It uses the &lt;code&gt;useFetch&lt;/code&gt; custom hook to fetch &lt;code&gt;product&lt;/code&gt; data and &lt;code&gt;ProductCard&lt;/code&gt; to render each product. The component handles &lt;code&gt;loading&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt; states and displays the products in a grid format.&lt;/p&gt;

&lt;p&gt;Update &lt;code&gt;pages/index.js&lt;/code&gt; with these lines of code.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ProductList&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../components/ProductList&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'max-w-screen-xl m-auto'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductList&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;Run the dev server with this command &lt;code&gt;yarn dev&lt;/code&gt;, it should start the server at &lt;code&gt;[http://localhost:3000](http://localhost:3000)&lt;/code&gt;, and you should see something like this on your browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fsammy365%2Fimage%2Fupload%2Fv1680880231%2FproductStore1_ias7b1.png%2520align%3D" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fsammy365%2Fimage%2Fupload%2Fv1680880231%2FproductStore1_ias7b1.png%2520align%3D" alt="browser displaying the product listing page of the demo application, featuring a grid of product cards with images, names, categories, and prices"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! We’ve been able to display the products. Let’s now implement the view for a single product. Update &lt;code&gt;pages/product/[id].js&lt;/code&gt; with these lines of code. Here we’ll use &lt;code&gt;useSWR&lt;/code&gt; directly.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useSWR&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;swr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;An error occurred while fetching the data.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ProductDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSWR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://fakestoreapi.com/products/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&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="nx"&gt;fetcher&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLoading&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Error: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"max-w-2xl mx-auto min-h-screen py-16"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-2xl font-semibold mb-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"md:flex space-x-8"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;
            &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;640&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"responsive"&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-gray-100 mb-2"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-xl font-bold mb-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;$&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getServerSideProps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;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;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&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="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ProductDetails&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This component handles &lt;code&gt;loading&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt; states and renders product information. The &lt;code&gt;getServerSideProps&lt;/code&gt; function retrieves the product ID from the query params, which is passed as a prop to the component for server-side rendering. A single product detail look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fsammy365%2Fimage%2Fupload%2Fv1680880483%2FProductDetails_snqnao.png%2520align%3D" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fsammy365%2Fimage%2Fupload%2Fv1680880483%2FProductDetails_snqnao.png%2520align%3D" alt="single product detail page on the demo application, showcasing a larger product image, name, category, price, and a detailed product description."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling Errors and Loading States
&lt;/h3&gt;

&lt;p&gt;SWR provides an &lt;code&gt;ErrorBoundary&lt;/code&gt; component that can be used to catch errors that occur within its child components. This component can be used to display a fallback UI when an error occurs, such as an error message or a reload button. The Error boundary provides a way to separate an application's data-fetching and data-rendering concerns. When an error occurs during data fetching, SWR Error Boundary can intercept and handle it gracefully. This way, error messages can be displayed to the user without breaking the entire application.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useSWR&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;swr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SWRConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;swr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ErrorBoundary&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-error-boundary&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;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSWR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to load data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;AppWrapper&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ErrorBoundary&lt;/span&gt; &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Failed to load data&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SWRConfig&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;SWRConfig&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ErrorBoundary&lt;/span&gt;&lt;span class="p"&gt;&amp;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;In this code snippet, if an error occurs during the fetching process, it throws an error, which is caught by the &lt;code&gt;ErrorBoundary&lt;/code&gt; component. The &lt;code&gt;fallback&lt;/code&gt; prop of the &lt;code&gt;ErrorBoundary&lt;/code&gt; component is a UI to be displayed when an error occurs. In this case, it displays a simple error message.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing loading indicators for a better user experience
&lt;/h3&gt;

&lt;p&gt;Let’s go back to building our app. Go to &lt;code&gt;component/LoadingIndicator.jsx&lt;/code&gt; and add these lines of code.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LoadingIndicator&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="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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-center items-center"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"animate-spin rounded-full h-10 w-10 border-b-2 border-blue-500"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;LoadingIndicator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;This LoadingIndicator component will be used to represent the loading states while fetching data. Head to &lt;code&gt;components/ProductList.jsx&lt;/code&gt; and modify the code to this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLoading&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoadingIndicator&lt;/span&gt; &lt;span class="p"&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;Let’s also create an ErrorMessage component that renders an error message when there are data fetching issues. Head to &lt;code&gt;components/ErrorMessage.jsx&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ErrorMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
      &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"alert"&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"font-bold"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Error: &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"block sm:inline"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;ErrorMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Update &lt;code&gt;component/ProductList.jsx&lt;/code&gt; like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ErrorMessage&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Optimistic UI updates
&lt;/h3&gt;

&lt;p&gt;When you POST a message in messaging apps, it is immediately displayed in the chat even if there’s no network connection. That’s the idea of optimistic UI updates, and this can be implemented with SWR. With SWR, you can update the local cache of the data in response to user actions while at the same time sending the update request to the server. If the server responds with an error, the local cache can be reverted back to its previous state. This way, the user gets an immediate response while still having the assurance that the data is being updated on the server.&lt;/p&gt;

&lt;p&gt;To implement optimistic UI updates with SWR, you need to use the &lt;code&gt;mutate&lt;/code&gt; function provided by the hook. The &lt;code&gt;mutate&lt;/code&gt; function allows you to update the cache of the data without making a request to the server. You can pass an updater function to the &lt;code&gt;mutate&lt;/code&gt; function, which receives the current data as an argument, and returns the updated data. The &lt;code&gt;mutate&lt;/code&gt; function updates the local cache with the new data, and triggers a re-render of the component.&lt;/p&gt;

&lt;p&gt;Once the update request is sent to the server, you can use the &lt;code&gt;mutate&lt;/code&gt; function again to update the cache with the response from the server. If the server responds with an error, you can use the &lt;code&gt;mutate&lt;/code&gt; function with the previous data to revert the cache to its previous state.&lt;/p&gt;

&lt;p&gt;This is how to implement optimistic UI updates in our app while uploading a new product.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pages/product/upload.js&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ProductUploadForm&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../components/ProductUploadForm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mutate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;swr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ErrorMessage&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../components/ErrorMessage&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;upload&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onUpload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newProduct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Optimistically update the local data&lt;/span&gt;
    &lt;span class="nf"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/products&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="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;products&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="nx"&gt;newProduct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;products&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="nx"&gt;newProduct&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Make the API call to create the product&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://fakestoreapi.com/products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newProduct&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Revalidate the data after successful upload&lt;/span&gt;
      &lt;span class="nf"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/products&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Handle any errors&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ErrorMessage&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"mb-4 text-2xl  p-3 font-extrabold text-gray-900 dark:text-white md:text-3xl lg:text-4xl"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&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="si"&gt;}&lt;/span&gt;
        Upload Product
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductUploadForm&lt;/span&gt; &lt;span class="na"&gt;onUpload&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onUpload&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Check out the &lt;code&gt;ProductUploadForm.jsx&lt;/code&gt; &lt;a href="https://github.com/Umoren/product-store-swr/blob/master/components/ProductUploadForm.jsx" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using SWR for paginated data fetching
&lt;/h3&gt;

&lt;p&gt;Paginated data fetching is one of the use cases of SWR. If we're fetching a large amount of data, breaking it down into smaller chunks called pages improves performance and reduces the amount of data transferred over the network.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;useSWRInfinite&lt;/code&gt; hook implements pagination with SWR. It takes two arguments: &lt;code&gt;getKey&lt;/code&gt; and &lt;code&gt;fetcher&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;getKey&lt;/code&gt; function returns a unique key for each page based on the page index and previous page data. Returning &lt;code&gt;null&lt;/code&gt; for an empty page prevents unnecessary requests.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fetcher&lt;/code&gt; function fetches data for a given key using an HTTP client, like &lt;code&gt;axios&lt;/code&gt; or &lt;code&gt;fetch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once we set up the &lt;code&gt;useSWRInfinite&lt;/code&gt; hook, we can use the &lt;code&gt;data&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt; properties to render the data and handle errors, respectively. We can also use the &lt;code&gt;isLoading&lt;/code&gt; property to show a loading indicator while data is being fetched.&lt;/p&gt;

&lt;p&gt;To implement pagination, we use the &lt;code&gt;size&lt;/code&gt; and &lt;code&gt;setSize&lt;/code&gt; properties to control the number of pages to fetch. Incrementing the &lt;code&gt;size&lt;/code&gt; value in a &lt;code&gt;loadMore&lt;/code&gt; function that's called when the user reaches the end of the current page enables pagination. We also use the &lt;code&gt;hasNextPage&lt;/code&gt; property to determine if more data can be fetched.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing infinite scrolling in Next.js with SWR
&lt;/h3&gt;

&lt;p&gt;Update the &lt;code&gt;useFetch&lt;/code&gt; hook with these lines of code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useSWRInfinite&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;swr/infinite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;An error occurred while fetching the data.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useFetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pageIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;previousPageData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;previousPageData&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;previousPageData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pageIndex&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`https://fakestoreapi.com/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;?_page=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pageNumber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;_limit=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;limit&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="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSize&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSWRInfinite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetcher&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loadMore&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="nf"&gt;setSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat&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="na"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;loadMore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;hasNextPage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;useFetch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In this code snippet is a custom hook that uses the SWR library to fetch paginated data. It takes two arguments, the &lt;code&gt;path&lt;/code&gt;, and &lt;code&gt;limit&lt;/code&gt; . The &lt;code&gt;getKey&lt;/code&gt; function returns a unique key for each page based on the page index and previous page data. The hook uses the &lt;code&gt;useSWRInfinite&lt;/code&gt; function to fetch data for a given key using an HTTP client. It returns an object with &lt;code&gt;data&lt;/code&gt;, &lt;code&gt;isLoading&lt;/code&gt;, &lt;code&gt;isError&lt;/code&gt; , &lt;code&gt;loadMore&lt;/code&gt; , and &lt;code&gt;hasNextPage&lt;/code&gt; properties. The &lt;code&gt;data&lt;/code&gt; property is an array of fetched data, while &lt;code&gt;isLoading&lt;/code&gt; is a boolean value that indicates if the data is being fetched. &lt;code&gt;isError&lt;/code&gt; is a boolean value that indicates if an error occurred while fetching data. &lt;code&gt;loadMore&lt;/code&gt; is a function that increments the number of pages to fetch, and &lt;code&gt;hasNextPage&lt;/code&gt; is a boolean value that indicates if there's more data to be fetched. The &lt;code&gt;fetcher&lt;/code&gt; function is called to fetch data from a given URL, and it throws an error if the response is not successful.&lt;/p&gt;

&lt;p&gt;The app should work properly with infinite scrolling now:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/GzPe_OeNYxY"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up…
&lt;/h2&gt;

&lt;p&gt;SWR is a powerful tool for efficient data fetching in client-side Next.js applications. Its built-in caching, automatic cache revalidation, error retries, support for real-time updates, and Hooks-based API make data management simpler and more streamlined. By using SWR, developers can improve the data-fetching experience for their users and ensure their applications are fast, reliable, and responsive. With Next.js, there are different methods of data fetching, depending on the desired rendering strategy. While client-side data fetching is a popular option, it can lead to several issues if not handled properly. SWR addresses these challenges and provides a more efficient way of managing data.&lt;/p&gt;

&lt;p&gt;Overall, SWR is a great tool for building high-performance, scalable, and reliable web applications with React and Next.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;SWR Official Documentation: &lt;a href="https://swr.vercel.app/docs/getting-started" rel="noopener noreferrer"&gt;&lt;strong&gt;https://swr.vercel.app/docs/getting-started&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Next.js Documentation on Data Fetching:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* Official guide on fetching data with Next.js: [**https://nextjs.org/docs/basic-features/data-fetching**](https://nextjs.org/docs/basic-features/data-fetching)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;An Introduction To SWR: React Hooks For Remote Data Fetching:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* [**https://www.smashingmagazine.com/2020/06/introduction-swr-react-hooks-remote-data-fetching/**](https://www.smashingmagazine.com/2020/06/introduction-swr-react-hooks-remote-data-fetching/)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Data fetching in React with SWR&lt;br&gt;&lt;br&gt;
&lt;a href="https://dev.to/ibrahimraimi/data-fetching-in-react-with-swr-5gb0"&gt;&lt;strong&gt;https://dev.to/ibrahimraimi/data-fetching-in-react-with-swr-5gb0&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GitHub Discussions and Conversations: &lt;strong&gt;&lt;a href="https://github.com/vercel/swr/discussions" rel="noopener noreferrer"&gt;https://github.com/vercel/swr/discussions&lt;/a&gt;&lt;/strong&gt;](&lt;a href="https://github.com/vercel/swr/discussions" rel="noopener noreferrer"&gt;https://github.com/vercel/swr/discussions&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>programming</category>
      <category>react</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Backstage.io: The Ultimate Developer Portal for Enhancing Team Efficiency and User Satisfaction.</title>
      <dc:creator>Samuel Umoren</dc:creator>
      <pubDate>Mon, 15 May 2023 16:32:15 +0000</pubDate>
      <link>https://dev.to/umoren/backstageio-the-ultimate-developer-portal-for-enhancing-team-efficiency-and-user-satisfaction-50f5</link>
      <guid>https://dev.to/umoren/backstageio-the-ultimate-developer-portal-for-enhancing-team-efficiency-and-user-satisfaction-50f5</guid>
      <description>&lt;p&gt;Developers are often the ultimate multitaskers. Picture a platform engineer's typical busy day: they might switch from writing UI code to modifying an API's authentication process, creating an Infrastructure as Code template, implementing continuous integration on a new project, working on a data pipeline, and updating documentation. Each task has its unique requirements, UIs, technologies, languages, or processes.&lt;/p&gt;

&lt;p&gt;This might sound thrilling for a sprinting stallion (yes, that's a nod to our friends at Pied Piper), but it's also a recipe for errors and potential chaos within an organization. Imagine being a chef trying to whip up several meal courses simultaneously. Pots are boiling, ingredients are strewn about, and multiple recipes are in progress. It's a high-energy environment, but it's also a chaotic one. The journey from a brilliant idea to polished software can feel the same. So, wouldn't it be great if there was a sous chef to bring some order to this chaos? That’s where Backstage comes in.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt; is an open platform designed for building developer portals developed by Spotify Engineering.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding &lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt;: A Powerful Developer Tool
&lt;/h3&gt;

&lt;p&gt;A developer portal acts like a Swiss army knife for developers. It streamlines the development process and fosters a community of developers. Spotify Engineering has leveraged this concept and introduced Backstage, an open platform designed for building developer portals.&lt;/p&gt;

&lt;p&gt;Consider the developer portal as a centralized hub for all services, software components, data, tools, and documentation within an organization. It simplifies navigation through the tech landscape and makes workflows more efficient.&lt;/p&gt;

&lt;p&gt;In essence, Backstage puts everything in one place. It simplifies tool management, streamlines workflows, and saves precious time. The goal? To make software development more efficient, so developers can focus on what they do best: crafting great software.&lt;/p&gt;

&lt;p&gt;This blog post will delve into how this handy tool can turbocharge your team's efficiency and make your users as thrilled as a kid in a candy store.&lt;/p&gt;

&lt;h2&gt;
  
  
  How &lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt; Boosts Software Development Efficiency Through Centralization, Automation, and Extensibility
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt; operates on a unique principle of centralizing all services, making them easily navigable and manageable for developers. The platform achieves this through its Software Catalog, a comprehensive list of all software components, resources, and tools available within an organization. You can think of the Catalog as an inventory of your organization's software. &lt;/p&gt;

&lt;p&gt;In more traditional setups, you most likely have to switch between multiple platforms and interfaces to access and manage different services. Backstage.io eliminates this by providing a unified interface that consolidates all these services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BCB73oBp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1684166621/Screenshot_2023-05-14_at_17.30.15_waeatr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BCB73oBp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1684166621/Screenshot_2023-05-14_at_17.30.15_waeatr.png" alt="The Service Catalog in [Backstage.io](http://backstage.io/), offering a comprehensive overview of all services within an organization." width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Service Catalog in &lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt;, offering a comprehensive overview of all services within an organization.&lt;/p&gt;

&lt;p&gt;With &lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt;, developers in your team can effortlessly locate and access any service they need within the Service Catalog. This catalog provides key information about each service, such as its owner, lifecycle status, and recent updates, offering an overview of all the services at a glance.&lt;/p&gt;

&lt;p&gt;One aspect of centralization in &lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt; is the TechDocs feature. TechDocs allows developers to create, maintain, find, and view documentation all within &lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt; itself. This means that alongside services, the corresponding documentation is also available in the same place, saving developers from searching in different locations.&lt;/p&gt;

&lt;p&gt;You can run your TechDocs separately from the organization dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U3ck98Al--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1684166621/Screenshot_2023-05-15_at_13.32.58_skn2gi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U3ck98Al--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1684166621/Screenshot_2023-05-15_at_13.32.58_skn2gi.png" alt="[Backstage.io](http://backstage.io/)'s TechDocs feature, centralizing the creation and viewing of documentation." width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt;'s TechDocs feature, centralizing the creation and viewing of documentation.&lt;/p&gt;

&lt;p&gt;Developers can view API documentation using this catalog plugin: &lt;code&gt;@backstage/plugin-api-docs&lt;/code&gt; like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S_OBjle5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1684166955/screencapture-localhost-3000-catalog-default-api-wayback-archive-definition-2023-05-14-18_13_20_hnk6jd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S_OBjle5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1684166955/screencapture-localhost-3000-catalog-default-api-wayback-archive-definition-2023-05-14-18_13_20_hnk6jd.png" alt="[Backstage.io](http://backstage.io/)'s API documentation, offering detailed information about each API's structure and capabilities." width="800" height="815"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt;'s API documentation, offering detailed information about each API's structure and capabilities.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt; also brings impressive automation capabilities to the table. It integrates with your existing CI/CD pipelines and providers (like Circle CI, GitHub Actions, and Jenkins), automatically updating each component's status in real-time. Moreover, it can automate routine tasks that developers often find themselves repeating. For instance, it can automatically set up new microservices or initiate test runs, freeing developers to focus on more critical tasks.&lt;/p&gt;

&lt;p&gt;Lastly, the plugin architecture of &lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt; allows for easy extensibility and customization. Need to add new functionality? Simply write a plugin and add it to your Backstage instance. Each plugin gets its page in Backstage, making it easy for developers to find and use the necessary tools without setting up new infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhancing User Satisfaction with &lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt;: Streamlining Workflows for Rapid Software Deployments
&lt;/h2&gt;

&lt;p&gt;User satisfaction extends beyond the end product; it encompasses the entire user journey. The efficiency and effectiveness of the development process shape a significant part of this journey. This is precisely where &lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt; makes its mark.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Streamlined Developer Experience
&lt;/h3&gt;

&lt;p&gt;Backstage.io offers a unified and streamlined developer experience where all services, libraries, APIs, and data pipelines are accessed from a single platform. This centralization eliminates the chaos of managing multiple tools, reduces the risk of miscommunication, and allows for a more cohesive approach to software development. &lt;/p&gt;

&lt;h3&gt;
  
  
  Rapid Iterations and Deployments
&lt;/h3&gt;

&lt;p&gt;With the accelerated development process Backstage.io enables, your team can roll out new features, enhancements, or fixes more quickly. This rapid iteration capability is particularly valuable in today's fast-paced digital world, where users expect continuous improvement and timely responses to their feedback. &lt;/p&gt;

&lt;h3&gt;
  
  
  Enhanced Communication and Collaboration
&lt;/h3&gt;

&lt;p&gt;Backstage.io brings tools and services together and fosters better communication and collaboration among developers. Developers can easily share, reuse, and collaborate on different project components with features like the Service Catalog and the Software Templates. This, in turn, contributes to more robust and reliable software, enhancing user satisfaction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--E8OMN5fc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1684166601/screencapture-localhost-3000-create-2023-05-14-18_31_48_fskjb4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--E8OMN5fc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1684166601/screencapture-localhost-3000-create-2023-05-14-18_31_48_fskjb4.png" alt="The interface for creating new services in [Backstage.io](http://backstage.io/), simplifying the process for developers." width="800" height="824"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The interface for creating new services in &lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt;, simplifying the process for developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reliable and Error-Free Software
&lt;/h3&gt;

&lt;p&gt;The unified view of Backstage.io allows for better tracking and monitoring of the entire software development lifecycle. It's easier to identify potential bottlenecks, catch bugs earlier, and ensure that the software being delivered is reliable and error-free. This results in a smoother user experience and increased user satisfaction. &lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt; helps in identifying potential bottlenecks, catching bugs earlier, and ensuring the delivery of reliable and error-free software through several features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Service Catalog: S&lt;/strong&gt;uch a comprehensive overview makes it easier to spot services that are facing issues or not performing as expected.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tech Insights:&lt;/strong&gt; Backstage.io includes features like Tech Radar, which help visualize, understand, and improve how software is built in the organization. It can be used to track metrics and set up checks or balances to ensure that certain standards or practices are being followed. This helps catch any potential issues early in the software development lifecycle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PlBDJZK5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1684166601/screencapture-localhost-3000-tech-radar-2023-05-15-15_09_03_uuc6ff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PlBDJZK5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1684166601/screencapture-localhost-3000-tech-radar-2023-05-15-15_09_03_uuc6ff.png" alt="[Backstage.io](http://backstage.io/)'s Tech Radar, visualizing and tracking how software is developed within the organization." width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt;'s Tech Radar, visualizing and tracking how software is developed within the organization.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Plugins&lt;/strong&gt;: &lt;a href="http://backstage.io/"&gt;Backstage.io&lt;/a&gt; supports various plugins that integrate with various tools for monitoring, logging, error tracking, continuous integration, and more. ********Some of these plugins include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD Plugins:&lt;/strong&gt; Backstage provides plugins for popular CI/CD tools like Jenkins, CircleCI, GitHub Actions, and Travis CI, among others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring and Observability Plugins:&lt;/strong&gt; Plugins like Grafana, Prometheus, and DataDog allow you to integrate your monitoring and observability tools with Backstage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Tracking Plugins:&lt;/strong&gt; Backstage supports plugins for error tracking and monitoring tools like Sentry, Rollbar, and Honeycomb.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Quality and Security Plugins:&lt;/strong&gt; Plugins like SonarQube, Snyk, and Lighthouse provide insights into code quality, security vulnerabilities, and performance issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Insights Plugin:&lt;/strong&gt; This plugin provides insights into your GitHub repositories, including statistics on pull requests, issues, and contributors.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In essence, Backstage.io does more than simplify the developer's workflow—it also significantly impacts user satisfaction. By enabling a more streamlined, rapid, and reliable development process, it ensures that your users receive the best experience possible.&lt;/p&gt;

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

&lt;p&gt;From my exploratory journey into the world of Backstage, it's clear that this platform has a lot to offer, even though I have yet to use it in a team setting. It has been designed thoughtfully to address the pain points that developers often encounter, particularly those working in large teams or on complex projects.&lt;/p&gt;

&lt;p&gt;Backstage streamlines workflows, centralize disparate tools and allows for easy extensibility - aspects that are crucial for efficiency in software development. These benefits simplify the developers' tasks and improve the overall user experience by enabling rapid iterations, reliable software, and efficient collaboration among teams.&lt;/p&gt;

&lt;p&gt;Let's consider how some notable companies have put Backstage into practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="http://netflix.com/"&gt;Netflix&lt;/a&gt;&lt;/strong&gt; uses Backstage as the front door to a unified experience, connecting their internal platform products across important workflows with integrated knowledge and support.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;&lt;a href="http://www.aa.com/"&gt;American Airlines&lt;/a&gt;&lt;/strong&gt;, Backstage is a central hub where developers develop and maintain applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="http://voiscooters.com/"&gt;Voi&lt;/a&gt;&lt;/strong&gt; has implemented Backstage as a Developer portal, the primary gateway to their infrastructure, documentation, and internal tooling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="http://www.fiverr.com/"&gt;Fiverr&lt;/a&gt;&lt;/strong&gt; has leveraged Backstage to unify separate tools developers use, like monitoring and dead letter queues management, into a single platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="http://www.hellofresh.de/"&gt;HelloFresh&lt;/a&gt;&lt;/strong&gt; has deployed Backstage as their Developer portal, used by over 500+ developers worldwide.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="http://soundcloud.com/"&gt;SoundCloud&lt;/a&gt;&lt;/strong&gt; uses the Developer portal to manage a catalog of people, services, documentation, feature toggles, escalation policies, and more.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="http://next.me/"&gt;Next&lt;/a&gt;&lt;/strong&gt; has utilized Backstage to improve the developer's experience by centralizing their services catalog and identifying microservices' ownership.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: The list of Backstage adopters and their use cases has been sourced from the official &lt;strong&gt;&lt;a href="https://github.com/backstage/backstage/blob/master/ADOPTERS.md"&gt;Backstage adopters list&lt;/a&gt;&lt;/strong&gt;. I have no personal affiliations with these organizations; these examples were selected purely based on their relevance and potential interest to readers.&lt;/p&gt;

&lt;p&gt;In conclusion, Backstage is a powerful tool that brings order to the often chaotic software development process. By centralizing services, automating workflows, and offering easy extensibility, Backstage paves the way for a smooth, efficient, and more manageable journey - akin to turning a rugged trail into a superhighway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;If you want to delve deeper into Backstage.io, here are some useful resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Backstage.io Documentation&lt;/strong&gt;: This is the official documentation of Backstage.io. It provides a comprehensive guide on how to get started, as well as details on various features and plugins. &lt;a href="https://backstage.io/docs/overview/what-is-backstage/"&gt;Link&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Building developer portals with Backstage&lt;/strong&gt;: This is an excellent tutorial video. It explains the basic concepts and guides you through setting up a Backstage.io instance. &lt;a href="//youtube.com/watch?v=_IJadjZg2Gw"&gt;Link&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Backstage.io GitHub Repository&lt;/strong&gt;: This repository contains the source code of Backstage.io. It is a useful resource if you want to contribute or understand how the platform works under the hood. &lt;a href="https://github.com/backstage/backstage"&gt;Link&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Building a Plugin in Backstage.io&lt;/strong&gt;: This tutorial offers an in-depth guide on how to build a custom plugin in Backstage.io. &lt;a href="https://www.youtube.com/watch?v=inN7e1hc-IQ"&gt;Link&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>productivity</category>
      <category>devops</category>
      <category>coding</category>
      <category>developers</category>
    </item>
    <item>
      <title>Step-by-Step Guide to Hosting and Deploying a React Static Website on AWS</title>
      <dc:creator>Samuel Umoren</dc:creator>
      <pubDate>Sat, 29 Apr 2023 22:32:43 +0000</pubDate>
      <link>https://dev.to/umoren/host-and-deploy-a-react-static-website-on-aws-7ed</link>
      <guid>https://dev.to/umoren/host-and-deploy-a-react-static-website-on-aws-7ed</guid>
      <description>&lt;p&gt;Throughout my years as a software developer specializing in frontend technologies, I've admired the simplicity of deploying static HTML, CSS, and JavaScript files to cloud hosting providers like Netlify, Vercel, or GitHub Pages. These platforms serve as Content Delivery Networks (CDNs) and automatically generate live URLs for hosted websites. Previously, I would use shared hosting or rent a Virtual Private Server (VPS) from providers like GoDaddy. Then, I would upload static files to the hosting server using an FTP client like Filezilla. Additionally, I needed to register a domain, or my website URL would default to the hosting server's IP address.&lt;/p&gt;

&lt;p&gt;Some organizations utilize cloud services like Amazon S3 or Azure Blob storage to store and serve files. However, this approach requires additional configuration, such as proper access permissions, MIME types, and cache headers.&lt;/p&gt;

&lt;p&gt;In a previous article on AWS CloudFormation, I suggested creating a static website hosted on Amazon S3 and distributed using Amazon CloudFront, using CloudFormation to provision and configure the S3 bucket and CloudFront distribution as a practice exercise.&lt;/p&gt;

&lt;p&gt;Building on that idea, I've developed this tutorial to guide you through hosting and deploying a React static website on AWS using services like S3, CloudFront, and CloudFormation. If you're unfamiliar with Amazon CloudFront, it's a content delivery network (CDN) service designed to enhance web applications' performance, availability, and security by distributing content to users from geographically closer edge locations.&lt;/p&gt;

&lt;h3&gt;
  
  
  How would we build this?
&lt;/h3&gt;

&lt;p&gt;This tutorial will use Amazon S3, Amazon CloudFront, and AWS CloudFormation to deploy a React-based static website. &lt;/p&gt;

&lt;p&gt;S3 will be used to store the compiled static website files generated from the React project. &lt;/p&gt;

&lt;p&gt;To enhance the performance and global reach of the website, we will use CloudFront CDN service to cache and serve content to users from edge locations closer to them.&lt;/p&gt;

&lt;p&gt;AWS CloudFormation will be the backbone of this setup, as it allows us to define the infrastructure for our static website as code. Creating a CloudFormation template will automate the provisioning and configuration of the S3 bucket and CloudFront distribution.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s the purpose?
&lt;/h3&gt;

&lt;p&gt;Using this combination of services, we are taking advantage of the scalability, security, and reliability offered by the AWS ecosystem. With the static website hosted on S3 and distributed through CloudFront, users will experience faster load times and better performance. At the same time, managing the infrastructure with CloudFormation ensures that the entire setup can be version-controlled, easily updated, and maintained systematically.  &lt;/p&gt;

&lt;p&gt;In other words, we are building a mini Vercel or Netlify for deploying static React apps. &lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-requisites
&lt;/h2&gt;

&lt;p&gt;It would be best if you had the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Account: Sign up for an AWS account if you don't have one already.&lt;/li&gt;
&lt;li&gt;AWS CLI: Install and configure the AWS CLI on your local machine. For instructions, follow this guide: &lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Code editor (preferably VS Code)&lt;/li&gt;
&lt;li&gt;Basic Knowledge of AWS CloudFormation&lt;/li&gt;
&lt;li&gt;Enable &lt;a href="https://us-east-1.console.aws.amazon.com/iam/home#/policies/arn:aws:iam::aws:policy/AmazonS3FullAccess" rel="noopener noreferrer"&gt;AmazonS3FullAccess&lt;/a&gt; and &lt;a href="https://us-east-1.console.aws.amazon.com/iam/home#/policies/arn:aws:iam::aws:policy/AWSCloudFormationFullAccess" rel="noopener noreferrer"&gt;AWSCloudFormationFullAccess&lt;/a&gt; Permission for your IAM user.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/download" rel="noopener noreferrer"&gt;Nodejs ≥v16&lt;/a&gt; installed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a React Project for AWS Deployment
&lt;/h2&gt;

&lt;p&gt;I found a beautiful todo app by &lt;strong&gt;&lt;a href="https://github.com/ilyasbelaoud" rel="noopener noreferrer"&gt;Ilyas Belaoud&lt;/a&gt;&lt;/strong&gt; on &lt;a href="https://github.com/ilyasbelaoud/react-todo-app" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, and we will use it for this project. To get started, clone the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/ilyasbelaoud/react-todo-app.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the dependencies by running this 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 &lt;span class="nb"&gt;install&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When it’s done installing, run the app with this 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 run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app will start at &lt;code&gt;[http://localhost:3000](http://localhost:3000)&lt;/code&gt; on your browser. You should see something like this: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fokbyoxypws6p2ludm8wq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fokbyoxypws6p2ludm8wq.png" alt="Screenshot 2023-04-29 at 12.40.52.png" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! We’ve set up the React app. &lt;/p&gt;

&lt;h2&gt;
  
  
  How to Set Up an Amazon S3 Bucket for Your React Website
&lt;/h2&gt;

&lt;p&gt;Let’s go ahead and create the S3 bucket to host the React app. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Write the CloudFormation template to create an S3 bucket&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To create an S3 bucket using CloudFormation, you must define the bucket resource in a YAML or JSON template file. In this example, I'll use YAML.&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code&gt;s3-react-static-website.yaml&lt;/code&gt; and add these lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;StaticWebsiteBucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::Bucket'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;random-unique-string&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This template defines a single S3 bucket resource named "StaticWebsiteBucket" with the specified bucket name. Replace &lt;code&gt;my-static-website-bucket&lt;/code&gt; with a unique bucket name of your choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Configure the bucket to act as a static website host&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To configure the S3 bucket for hosting a static website, add the &lt;code&gt;WebsiteConfiguration&lt;/code&gt; property to the bucket definition in the CloudFormation template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;StaticWebsiteBucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::Bucket'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;random-unique-string&amp;gt;&lt;/span&gt;
            &lt;span class="s"&gt;PublicAccessBlockConfiguration&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;BlockPublicAcls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;BlockPublicPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;IgnorePublicAcls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;RestrictPublicBuckets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;WebsiteConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;IndexDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
        &lt;span class="na"&gt;ErrorDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;404.html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we've specified &lt;code&gt;index.html&lt;/code&gt; as the index document and &lt;code&gt;404.html&lt;/code&gt; as the error document. Adjust the file names according to your project structure.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;PublicAccessBlockConfiguration&lt;/code&gt; property is important because all Block Public Access settings are automatically enabled when a new bucket is created. So for the purpose of this demo, we are setting them to false.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Set up appropriate bucket policies for public access&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To grant public read access to the objects in your S3 bucket, you need to create an S3 bucket policy. Add the following resource to your CloudFormation template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ... (previous resources) ...&lt;/span&gt;

  &lt;span class="na"&gt;StaticWebsiteBucketPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::BucketPolicy'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;StaticWebsiteBucket&lt;/span&gt;
      &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:GetObject'&lt;/span&gt;
            &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:s3:::${StaticWebsiteBucket}/*'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This policy allows public &lt;code&gt;GetObject&lt;/code&gt; access to all objects in the bucket. The &lt;code&gt;!Ref&lt;/code&gt; function is used to reference the bucket created earlier, and the &lt;code&gt;!Sub&lt;/code&gt; function is used to substitute the bucket name into the resource ARN.&lt;/p&gt;

&lt;p&gt;This is the complete Yaml code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2010-09-09'&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Create&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;an&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;S3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;bucket&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hosting&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;static&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;React&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;website'&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;StaticWebsiteBucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::Bucket'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-static-website-bucket&lt;/span&gt;
            &lt;span class="s"&gt;PublicAccessBlockConfiguration&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;BlockPublicAcls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;BlockPublicPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;IgnorePublicAcls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;RestrictPublicBuckets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
            &lt;span class="s"&gt;WebsiteConfiguration&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;IndexDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
        &lt;span class="na"&gt;ErrorDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;404.html&lt;/span&gt;

  &lt;span class="na"&gt;StaticWebsiteBucketPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::BucketPolicy'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;StaticWebsiteBucket&lt;/span&gt;
      &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:GetObject'&lt;/span&gt;
            &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:s3:::${StaticWebsiteBucket}/*'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Amazon CloudFront Setup for React Static Website
&lt;/h2&gt;

&lt;p&gt;In this section, we will set up the CloudFront distribution. These are the steps we will take:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write the CloudFormation template to create an S3 bucket&lt;/li&gt;
&lt;li&gt;Configure the distribution to use the S3 bucket as the origin&lt;/li&gt;
&lt;li&gt;Set up caching, SSL, and other settings as needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s get started.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Write the CloudFormation template to create a CloudFront distribution&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To create a CloudFront distribution using CloudFormation, you must define the distribution resource in the YAML template file. Update the existing YAML file that you used to set up the S3 bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ... (previous resources) ...&lt;/span&gt;

  &lt;span class="na"&gt;StaticWebsiteCloudFrontDistribution&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::CloudFront::Distribution'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DistributionConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;DefaultRootObject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
        &lt;span class="na"&gt;Origins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;DomainName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;StaticWebsiteBucket.RegionalDomainName&lt;/span&gt;
            &lt;span class="na"&gt;Id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StaticWebsiteS3Origin&lt;/span&gt;
            &lt;span class="na"&gt;S3OriginConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;OriginAccessIdentity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This template defines a single CloudFront distribution resource named &lt;code&gt;StaticWebsiteCloudFrontDistribution&lt;/code&gt;. The distribution is enabled and configured to use the S3 bucket as its origin.&lt;/p&gt;

&lt;p&gt;Let me break down the relevant parts of this code snippet.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Properties&lt;/code&gt; section is where you define the properties of the CloudFront distribution. The &lt;code&gt;DistributionConfig&lt;/code&gt; property contains the configuration settings for the CloudFront distribution.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Enabled&lt;/code&gt; property is set to "true", which means that the CloudFront distribution is active.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;DefaultRootObject&lt;/code&gt; property is set to &lt;code&gt;index.html&lt;/code&gt;. This means that when a user accesses the root of your CloudFront distribution, CloudFront will return the &lt;code&gt;index.html&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Origins&lt;/code&gt; property lists origins for the CloudFront distribution. In this case, there's only one origin: the S3 bucket you created earlier for hosting the static website.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DomainName&lt;/code&gt;: The domain name of the S3 bucket. &lt;code&gt;!GetAtt StaticWebsiteBucket.RegionalDomainName&lt;/code&gt; is a CloudFormation function that retrieves the regional domain name of the S3 bucket you created earlier.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Id&lt;/code&gt;: A unique identifier for the origin, in this case, "StaticWebsiteS3Origin".&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;S3OriginConfig&lt;/code&gt;: Configuration settings specific to the S3 origin. &lt;code&gt;OriginAccessIdentity&lt;/code&gt; is set to an empty string, meaning the CloudFront distribution will use the S3 bucket's public access settings.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Configure the distribution to use the S3 bucket as the origin&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;In the YAML file, we've already configured the CloudFront distribution to use the S3 bucket created earlier as its origin. The &lt;code&gt;!GetAtt&lt;/code&gt; function is used to get the regional domain name of the S3 bucket.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Set up caching, SSL, and other settings as needed&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For caching and other settings, you can configure the &lt;code&gt;CacheBehaviors&lt;/code&gt; and &lt;code&gt;DefaultCacheBehavior&lt;/code&gt; properties of the distribution. You can add an ACM (AWS Certificate Manager) certificate ARN to the &lt;code&gt;ViewerCertificate&lt;/code&gt; property to enable SSL. However, seeing as we don’t have a  custom domain, we will not include the &lt;code&gt;ViewerCertificate&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Does this mean the website connection will be unsafe using HTTP? No, it doesn’t. The CloudFront distribution will use the default CloudFront domain (e.g., &lt;code&gt;d12345abcd12345.cloudfront.net&lt;/code&gt;). The connection will still be secure using HTTPS with the default CloudFront certificate, but the URL will not use a custom domain.&lt;/p&gt;

&lt;p&gt;Update your Yaml file with these lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# ... (previous resources) ...&lt;/span&gt;

  &lt;span class="na"&gt;StaticWebsiteCloudFrontDistribution&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::CloudFront::Distribution'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DistributionConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;DefaultRootObject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
        &lt;span class="na"&gt;Origins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;DomainName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;StaticWebsiteBucket.RegionalDomainName&lt;/span&gt;
            &lt;span class="na"&gt;Id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StaticWebsiteS3Origin&lt;/span&gt;
            &lt;span class="na"&gt;S3OriginConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;OriginAccessIdentity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
        &lt;span class="na"&gt;DefaultCacheBehavior&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;AllowedMethods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;HEAD&lt;/span&gt;
          &lt;span class="na"&gt;CachedMethods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;HEAD&lt;/span&gt;
          &lt;span class="na"&gt;Compress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;ForwardedValues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;QueryString&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
            &lt;span class="na"&gt;Cookies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Forward&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;none&lt;/span&gt;
          &lt;span class="na"&gt;MinTTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3600&lt;/span&gt;
          &lt;span class="na"&gt;MaxTTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;86400&lt;/span&gt;
          &lt;span class="na"&gt;DefaultTTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;86400&lt;/span&gt;
          &lt;span class="na"&gt;TargetOriginId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StaticWebsiteS3Origin&lt;/span&gt;
          &lt;span class="na"&gt;ViewerProtocolPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redirect-to-https&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration sets up caching, enables Gzip compression, and enforces HTTPS. Let’s break down some of the relevant parts of this code snippet. You can notice some properties you’ve used previously repeated, right? I think this is how you get to learn faster. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;DefaultCacheBehavior&lt;/code&gt; property is used to configure how CloudFront processes requests and caches content. In this case, the &lt;code&gt;AllowedMethods&lt;/code&gt; property specifies which HTTP methods are allowed for requests. We only allow "GET" and "HEAD" methods since it's a static website; these are the only methods needed to fetch content.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;CachedMethods&lt;/code&gt; property lists the HTTP methods for which CloudFront caches the responses. We cache &lt;code&gt;GET&lt;/code&gt; and "HEAD" responses to serve content faster to users from edge locations.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Compress&lt;/code&gt; property is set to &lt;code&gt;true&lt;/code&gt;, which enables automatic Gzip compression of files. This reduces the transmitted files' size, resulting in faster load times and reduced data transfer costs.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;ForwardedValues&lt;/code&gt; property specifies how CloudFront forwards query strings and cookies to the origin (the S3 bucket in this case). In this configuration, we set &lt;code&gt;QueryString&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;, meaning CloudFront does not forward query strings to the origin. We set Forward to none for the &lt;code&gt;Cookies&lt;/code&gt; property, indicating that CloudFront does not forward any cookies to the origin.&lt;/li&gt;
&lt;li&gt;These properties set the Time-to-Live (TTL) values for cached objects in seconds: &lt;code&gt;MinTTL&lt;/code&gt; is the minimum amount of time that objects stay in the cache, &lt;code&gt;MaxTTL&lt;/code&gt; is the maximum time, and &lt;code&gt;DefaultTTL&lt;/code&gt; is the default time if no other value is specified in the cache-control header from the origin. In this configuration, the minimum TTL is set to 1 hour (3600 seconds), the maximum TTL is set to 24 hours (86400 seconds), and the default TTL is also set to 24 hours (86400 seconds).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;TargetOriginId&lt;/code&gt; property specifies the origin (in this case, the S3 bucket) to which CloudFront forwards requests. The value "StaticWebsiteS3Origin" matches the "Id" value specified in the "Origins" property earlier.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;ViewerProtocolPolicy&lt;/code&gt; property sets how CloudFront handles HTTP and HTTPS requests from users. In this case, "redirect-to-https" means that CloudFront automatically redirects HTTP requests to HTTPS, ensuring users access the site securely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So far, this is how our Yaml file looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2010-09-09'&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;CloudFormation&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Template:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Static&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Website&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;React,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Amazon&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;S3,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Amazon&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;CloudFront'&lt;/span&gt;
&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;StaticWebsiteBucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::Bucket'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-unique-bucket-name&lt;/span&gt;
            &lt;span class="s"&gt;PublicAccessBlockConfiguration&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;BlockPublicAcls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;BlockPublicPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;IgnorePublicAcls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="na"&gt;RestrictPublicBuckets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="na"&gt;WebsiteConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;IndexDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
        &lt;span class="na"&gt;ErrorDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;error.html&lt;/span&gt;

  &lt;span class="na"&gt;StaticWebsiteBucketPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::BucketPolicy'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;StaticWebsiteBucket&lt;/span&gt;
      &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublicReadForWebsiteContent&lt;/span&gt;
        &lt;span class="na"&gt;Version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2012-10-17'&lt;/span&gt;
        &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Sid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublicRead&lt;/span&gt;
            &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
            &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
            &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:GetObject'&lt;/span&gt;
            &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Sub&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;arn:aws:s3:::${StaticWebsiteBucket}/*'&lt;/span&gt;

  &lt;span class="na"&gt;StaticWebsiteCloudFrontDistribution&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::CloudFront::Distribution'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DistributionConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;DefaultRootObject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
        &lt;span class="na"&gt;Origins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;DomainName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;StaticWebsiteBucket.RegionalDomainName&lt;/span&gt;
            &lt;span class="na"&gt;Id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StaticWebsiteS3Origin&lt;/span&gt;
            &lt;span class="na"&gt;S3OriginConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;OriginAccessIdentity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
        &lt;span class="na"&gt;DefaultCacheBehavior&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;AllowedMethods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;HEAD&lt;/span&gt;
          &lt;span class="na"&gt;CachedMethods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;HEAD&lt;/span&gt;
          &lt;span class="na"&gt;Compress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;ForwardedValues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;QueryString&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
            &lt;span class="na"&gt;Cookies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;Forward&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;none&lt;/span&gt;
          &lt;span class="na"&gt;MinTTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3600&lt;/span&gt;
          &lt;span class="na"&gt;MaxTTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;86400&lt;/span&gt;
          &lt;span class="na"&gt;DefaultTTL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;86400&lt;/span&gt;
          &lt;span class="na"&gt;TargetOriginId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;StaticWebsiteS3Origin&lt;/span&gt;
          &lt;span class="na"&gt;ViewerProtocolPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redirect-to-https&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploying Your CloudFormation Stack on AWS
&lt;/h2&gt;

&lt;p&gt;This section will deploy the cloud formation stack using the AWS CLI. &lt;/p&gt;

&lt;p&gt;Follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Run the following AWS CLI command to create the CloudFormation stack:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudformation create-stack &lt;span class="nt"&gt;--stack-name&lt;/span&gt; &amp;lt;YourPreferredStackName&amp;gt; &lt;span class="nt"&gt;--template-body&lt;/span&gt; file://s3-react-static-website.yaml
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;It might take some minutes to deploy, depending on your internet connection. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To verify the stack creation progress, run the following command:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudformation describe-stacks &lt;span class="nt"&gt;--stack-name&lt;/span&gt; &amp;lt;YourStackName&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;If it’s deployed successfully, you should see this on your terminal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fy1rc7zftanqe5p1rda6f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fy1rc7zftanqe5p1rda6f.png" alt="Screenshot 2023-04-29 at 21.13.57.png" width="800" height="199"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also verify from your cloud formation console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F2bsg4qeewwft3ykicmgw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F2bsg4qeewwft3ykicmgw.png" alt="Screenshot 2023-04-29 at 21.14.39.png" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Awesome!&lt;/p&gt;

&lt;h2&gt;
  
  
  Building and Uploading Your React App to S3
&lt;/h2&gt;

&lt;p&gt;Now that your CloudFormation stack has been successfully deployed and the S3 bucket and CloudFront distribution are set up, it's time to build and upload your React app.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Build the React app&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Navigate to your React app's root directory in your terminal and run the following command to create a production-ready build of your React app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command generates a &lt;code&gt;build&lt;/code&gt; folder in your project directory containing the minified and optimized files necessary for deploying your React app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Flqood58y817t8gp00gqg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Flqood58y817t8gp00gqg.png" alt="Screenshot 2023-04-29 at 22.32.59.png" width="800" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Upload the contents of the &lt;code&gt;build&lt;/code&gt; folder to the S3 bucket&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let’s now use the AWS CLI to upload the build folder contents to the s3 bucket. &lt;/p&gt;

&lt;p&gt;Navigate to the &lt;code&gt;build&lt;/code&gt; folder in your React app's directory. Run the following command to sync the contents of the &lt;code&gt;build&lt;/code&gt; folder to your S3 bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3 &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; s3://your-bucket-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find your bucket name in your s3 dashboard. You should get something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fmds9cxnqjm2zr77w33xa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fmds9cxnqjm2zr77w33xa.png" alt="Screenshot 2023-04-29 at 22.26.37.png" width="800" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After uploading the contents of the &lt;code&gt;build&lt;/code&gt; folder to the S3 bucket, your React static website should be accessible through the CloudFront distribution URL. You can find the URL in the Amazon CloudFront console under the "Domain Name" column for your distribution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F5x22n13lo6zwn85syb0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F5x22n13lo6zwn85syb0w.png" alt="Screenshot 2023-04-29 at 22.38.25.png" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And yes, it works!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F99o2r4o9ssbpydngel3m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F99o2r4o9ssbpydngel3m.png" alt="Screenshot 2023-04-29 at 22.39.09.png" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Leveraging AWS for Frontend React Projects: Final Thoughts
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we've gone through the step-by-step process of hosting and deploying a React static website on AWS using S3, CloudFront, and CloudFormation. We've learned to create and configure an S3 bucket for hosting our website files, set up a CloudFront distribution to optimize performance, and used CloudFormation to automate the provisioning and management of these resources.&lt;/p&gt;

&lt;p&gt;By leveraging these AWS services, we've successfully deployed a scalable, high-performance, and globally accessible React static website. We've also familiarized ourselves with the AWS ecosystem, gaining valuable skills and knowledge that can be applied to future projects.&lt;/p&gt;

&lt;p&gt;This tutorial serves as a solid foundation for more advanced use cases, enabling you to harness the full potential of AWS CloudFormation Infrastructure as Code Service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Resources for Further Learning
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html#aws-properties-s3-bucket--examples" rel="noopener noreferrer"&gt;AWS CloudFormation Guide on AWS::S3::Bucket&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html" rel="noopener noreferrer"&gt;Blocking public access to your Amazon S3 storage&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html" rel="noopener noreferrer"&gt;AWS CloudFront Developer Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>aws</category>
      <category>cloud</category>
      <category>infrastructureascode</category>
    </item>
    <item>
      <title>Getting Started with Infrastructure as Code: AWS CloudFormation</title>
      <dc:creator>Samuel Umoren</dc:creator>
      <pubDate>Wed, 19 Apr 2023 20:21:10 +0000</pubDate>
      <link>https://dev.to/umoren/getting-started-with-infrastructure-as-code-aws-cloudformation-3m0e</link>
      <guid>https://dev.to/umoren/getting-started-with-infrastructure-as-code-aws-cloudformation-3m0e</guid>
      <description>&lt;p&gt;In my previous article, I introduced Infrastructure as Code (IaC) and discussed its key benefits, including automation, consistency, version control, scalability, and cost savings. That article focused on Terraform, an open-source IaC tool that enables defining, managing, and provisioning cloud infrastructure using the HashiCorp Configuration Language (HCL). I provided a step-by-step guide to creating an EC2 instance with Terraform, which you can find &lt;a href="https://dev.to/umoren/getting-started-with-infrastructure-as-code-iac-terraform-4mac"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this article, we'll shift our focus to &lt;strong&gt;AWS CloudFormation&lt;/strong&gt;, a widely-used IaC tool in the AWS ecosystem. AWS CloudFormation allows you to model, provision, and manage your AWS infrastructure using JSON or YAML templates. It offers a consistent, automated approach to managing infrastructure, enabling you to create and update resources in a controlled and predictable manner.&lt;/p&gt;

&lt;p&gt;Throughout this article, we'll cover the basics of AWS CloudFormation, demonstrating how to create and manage AWS resources using this powerful tool. By the end of this article, you'll better understand AWS CloudFormation and be equipped to decide if it's the right IaC solution for your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why AWS CloudFormation?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Imagine building a web app on AWS, using EC2 for compute, S3 for storage, RDS for managed databases, and Lambda for API requests. Managing these resources separately and configuring them to work together can be complex and time-consuming.&lt;/p&gt;

&lt;p&gt;As your application gains traction globally, availability becomes crucial. To improve it, you can replicate the application in multiple AWS regions. This ensures users can access your app even if one region experiences an outage. However, replication involves duplicating resources and configurations across regions, which can be challenging.&lt;/p&gt;

&lt;p&gt;This is where AWS CloudFormation comes in:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solving app replication:&lt;/strong&gt; CloudFormation templates define resources and configurations, allowing you to provision identical resources in different regions consistently and repeatedly. This ensures consistency and reduces configuration drift.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solving resource management:&lt;/strong&gt; CloudFormation automates resource provisioning and configuration, making it easier to create, update, and delete resources. Deleting a stack removes all associated resources, ensuring efficient cleanup.&lt;/p&gt;

&lt;p&gt;By using CloudFormation, managing a collection of resources as a single unit becomes simpler, streamlining the process of maintaining consistency across your infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  CloudFormation Concepts
&lt;/h2&gt;

&lt;p&gt;In AWS CloudFormation, stacks and templates are essential components to define and manage your infrastructure. Let's use an analogy to help illustrate these concepts:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Templates&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Imagine that you're constructing a building. A template can be thought of as the blueprint for your building. The blueprint contains detailed information about the structure, such as the number of floors, rooms, doors, and windows. Similarly, a CloudFormation template is a JSON or YAML formatted text file that describes the AWS resources and their properties required for your application or infrastructure.&lt;/p&gt;

&lt;p&gt;Here's a CloudFormation example template in YAML format that defines an Amazon S3 bucket:&lt;/p&gt;

&lt;p&gt;YAML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;S3Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::S3::Bucket&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-sample-bucket&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or JSON:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Resources"&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;"S3Bucket"&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;"Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AWS::S3::Bucket"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Properties"&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;"BucketName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-sample-bucket"&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="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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Stacks&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Continuing with the building analogy, a stack can be thought of as the actual constructed building based on the blueprint. It is a collection of AWS resources that are created and managed as a single unit when you provide the template. In the context of CloudFormation, a stack is the live instantiation of a template, where each resource defined in the template is created and associated with the stack. &lt;/p&gt;

&lt;p&gt;For instance, when you create a stack using the example template above, CloudFormation will create an S3 bucket with the specified name &lt;strong&gt;&lt;code&gt;my-example-bucket&lt;/code&gt;&lt;/strong&gt;. The stack will be responsible for managing the lifecycle of this bucket, such as updating its properties or deleting it when the stack is deleted.&lt;/p&gt;

&lt;p&gt;To provide more clarity, when you build web or mobile applications using Amplify and select resources like Cognito for authentication, Analytics for notifications, Datastore for GraphQL APIs, or any other resources for your app, Amplify automatically creates CloudFormation templates based on your choices. These templates are then deployed to an S3 bucket, and CloudFormation is used to create, update, or delete the corresponding stack.&lt;/p&gt;

&lt;p&gt;In other words, Amplify simplifies the process of managing AWS resources for your application by generating and managing CloudFormation templates on your behalf. This makes it easier to deploy and manage your application's infrastructure.&lt;/p&gt;

&lt;p&gt;When you look at your CloudFormation dashboard, you will see the stacks that have been created or managed by Amplify, along with any other stacks you've created manually or through other means.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--l8KvTxyY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681934380/Screenshot_2023-04-19_at_16.25.30_m4lsny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--l8KvTxyY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681934380/Screenshot_2023-04-19_at_16.25.30_m4lsny.png" alt="Screenshot 2023-04-19 at 16.25.30.png" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find the Template on the public &lt;a href="https://gist.github.com/Umoren/afc57c954817f37dd4062dd0bf26f39c"&gt;gist&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Change Sets
&lt;/h3&gt;

&lt;p&gt;Imagine you have a blueprint (your CloudFormation template) of a house (your stack). The blueprint contains all the details about the house, such as the number of rooms, the layout, and the materials used for construction.&lt;/p&gt;

&lt;p&gt;Now, you decide to make some changes to the house, like adding a room or changing the layout of the living room. Instead of immediately making the changes to the house itself, you first update the blueprint to reflect the desired changes. Change sets in AWS CloudFormation are like comparing the updated blueprint with the original one. They allow you to see the differences between the two versions, such as the new room or the altered living room layout.&lt;/p&gt;

&lt;p&gt;With the change sets, you can review the changes and understand how they will impact the house before actually making the updates. This way, you can be sure that you're making the right modifications without causing any unintended damage or altering the house in an undesired manner. After reviewing the change set (the differences between the two blueprints), you can decide whether to apply the changes to the house or cancel them. If you're satisfied with the changes, you can update the house according to the new blueprint. If you decide against the modifications, you can discard the change set and leave your house unchanged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating your first AWS CloudFormation Template
&lt;/h2&gt;

&lt;p&gt;You should have these prerequisites to follow through with the rest of this article.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Account: Sign up for an AWS account if you don't have one already.&lt;/li&gt;
&lt;li&gt;AWS CLI: Install and configure the AWS CLI on your local machine. For instructions, follow this guide: &lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html"&gt;https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Code editor (preferably VS Code)&lt;/li&gt;
&lt;li&gt;Enable &lt;a href="https://us-east-1.console.aws.amazon.com/iam/home#/policies/arn:aws:iam::aws:policy/AmazonS3FullAccess"&gt;AmazonS3FullAccess&lt;/a&gt; and &lt;a href="https://us-east-1.console.aws.amazon.com/iam/home#/policies/arn:aws:iam::aws:policy/AWSCloudFormationFullAccess"&gt;AWSCloudFormationFullAccess&lt;/a&gt; Permission for your IAM user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s cover the basic components of a CloudFormation template and walk through an example of creating an S3 bucket. If you went through this &lt;a href="https://gist.github.com/Umoren/afc57c954817f37dd4062dd0bf26f39c"&gt;GitHub Gist&lt;/a&gt; that contains a sample template, you’ll notice these components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resources&lt;/strong&gt;: The core component of a CloudFormation template is the resources section, where you define the AWS resources you want to create and manage. Each resource has a unique logical ID and a resource type that identifies the AWS service it belongs to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameters&lt;/strong&gt;: CloudFormation parameters allow you to pass values into your template, making it customizable and reusable. They enable you to specify dynamic values, such as the name of the S3 bucket or the instance type for an EC2 instance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outputs&lt;/strong&gt;: CloudFormation outputs are used to export values from the created resources, making them accessible outside of the stack. They can be used to display specific information, like the URL of a created S3 bucket or the public IP address of an EC2 instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create an &lt;code&gt;s3_bucket_template.yaml&lt;/code&gt; file and add these lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2010-09-09'&lt;/span&gt;
&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;AWS&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;CloudFormation&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;template&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;create&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;an&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;S3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;bucket'&lt;/span&gt;

&lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;The&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;S3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;bucket'&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;

&lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;MyS3Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::Bucket'&lt;/span&gt;
    &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;BucketName&lt;/span&gt;

&lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;BucketURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;The&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;URL&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;created&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;S3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;bucket'&lt;/span&gt;
    &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;MyS3Bucket.WebsiteURL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this template:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We define a parameter &lt;strong&gt;&lt;code&gt;BucketName&lt;/code&gt;&lt;/strong&gt; of type String to allow users to specify the desired name for the S3 bucket.&lt;/li&gt;
&lt;li&gt;Under the &lt;strong&gt;&lt;code&gt;Resources&lt;/code&gt;&lt;/strong&gt; section, we define a resource of type &lt;strong&gt;&lt;code&gt;AWS::S3::Bucket&lt;/code&gt;&lt;/strong&gt; with a logical ID &lt;strong&gt;&lt;code&gt;MyS3Bucket&lt;/code&gt;&lt;/strong&gt;. We use the &lt;strong&gt;&lt;code&gt;!Ref&lt;/code&gt;&lt;/strong&gt; function to reference the value of the &lt;strong&gt;&lt;code&gt;BucketName&lt;/code&gt;&lt;/strong&gt; parameter.&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;&lt;code&gt;Outputs&lt;/code&gt;&lt;/strong&gt; section, we define an output &lt;strong&gt;&lt;code&gt;BucketURL&lt;/code&gt;&lt;/strong&gt; that displays the URL of the created S3 bucket using the &lt;strong&gt;&lt;code&gt;!GetAtt&lt;/code&gt;&lt;/strong&gt; function to retrieve the &lt;strong&gt;&lt;code&gt;WebsiteURL&lt;/code&gt;&lt;/strong&gt; attribute from the &lt;strong&gt;&lt;code&gt;MyS3Bucket&lt;/code&gt;&lt;/strong&gt; resource.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To deploy the CloudFormation template you just created, follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Create a CloudFormation stack using the AWS CLI and your template file. Replace &lt;strong&gt;&lt;code&gt;&amp;lt;STACK_NAME&amp;gt;&lt;/code&gt;&lt;/strong&gt; with a unique name for your stack and &lt;strong&gt;&lt;code&gt;&amp;lt;BUCKET_NAME&amp;gt;&lt;/code&gt;&lt;/strong&gt; with the desired name for your S3 bucket:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudformation create-stack &lt;span class="nt"&gt;--stack-name&lt;/span&gt; &amp;lt;STACK_NAME&amp;gt; &lt;span class="nt"&gt;--template-body&lt;/span&gt; file://s3_bucket_template.yaml &lt;span class="nt"&gt;--parameters&lt;/span&gt; &lt;span class="nv"&gt;ParameterKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;BucketName,ParameterValue&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;BUCKET_NAME&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ensure the s3 bucket name is very unique. Something like this &lt;code&gt;my-unique-bucket-8f3gh1t2-202434537&lt;/code&gt; to avoid the stack creation from failing. &lt;/p&gt;

&lt;p&gt;💡 &lt;strong&gt;&lt;em&gt;To generate a unique name, you should use a combination of a prefix, a random string, and a timestamp.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The AWS CLI command will return a &lt;strong&gt;&lt;code&gt;StackId&lt;/code&gt;&lt;/strong&gt; in the response, indicating that the stack creation has started. You can monitor the progress of the stack creation in the &lt;strong&gt;&lt;a href="https://console.aws.amazon.com/cloudformation/"&gt;AWS CloudFormation console&lt;/a&gt;&lt;/strong&gt;.
If the stack creation fails, navigate to the &lt;strong&gt;Events&lt;/strong&gt; tab and check the log.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the stack creation is complete, you can retrieve the output value for the &lt;strong&gt;&lt;code&gt;BucketURL&lt;/code&gt;&lt;/strong&gt; by running the following command, replacing &lt;strong&gt;&lt;code&gt;&amp;lt;STACK_NAME&amp;gt;&lt;/code&gt;&lt;/strong&gt; with the name of your stack:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudformation describe-stacks &lt;span class="nt"&gt;--stack-name&lt;/span&gt; &amp;lt;STACK_NAME&amp;gt; &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Stacks[0].Outputs[?OutputKey=='BucketURL'].OutputValue"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text

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

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should see the &lt;strong&gt;output&lt;/strong&gt; like this on your terminal&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1myv_cDw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681934378/Screenshot_2023-04-19_at_17.54.25_ri7gfs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1myv_cDw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681934378/Screenshot_2023-04-19_at_17.54.25_ri7gfs.png" alt="Screenshot 2023-04-19 at 17.54.25.png" width="800" height="108"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or on your Amazon S3 console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q2pcbzxu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681934381/Screenshot_2023-04-19_at_18.00.01_ofylan.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q2pcbzxu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681934381/Screenshot_2023-04-19_at_18.00.01_ofylan.png" alt="Screenshot 2023-04-19 at 18.00.01.png" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the purpose of the article, i won’t be creating any objects (🙂).&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating the CloudFormation Template
&lt;/h2&gt;

&lt;p&gt;In the S3 bucket example, let's say you want to update the stack to enable versioning for the created S3 bucket. Versioning is a feature in Amazon S3 that allows you to preserve, retrieve, and restore every version of every object in your bucket.&lt;/p&gt;

&lt;p&gt;You will have to modify the CloudFormation template to include the versioning configuration and then update the stack using the AWS CLI.&lt;/p&gt;

&lt;p&gt;Add the &lt;code&gt;VersioningConfiguration&lt;/code&gt; property to the &lt;code&gt;MyS3Bucket&lt;/code&gt; resource in your &lt;code&gt;s3_bucket_template.yaml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2010-09-09'&lt;/span&gt;
    &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;AWS&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;CloudFormation&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;template&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;create&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;an&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;S3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;bucket'&lt;/span&gt;

    &lt;span class="na"&gt;Parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;The&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;S3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;bucket'&lt;/span&gt;
        &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;String&lt;/span&gt;
        &lt;span class="na"&gt;Default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MyS3Bucket&lt;/span&gt;

    &lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MyS3Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AWS::S3::Bucket'&lt;/span&gt;
        &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;BucketName&lt;/span&gt;
          &lt;span class="na"&gt;VersioningConfiguration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;Status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enabled&lt;/span&gt;

    &lt;span class="na"&gt;Outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;BucketURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;The&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;URL&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;created&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;S3&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;bucket'&lt;/span&gt;
        &lt;span class="na"&gt;Value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!GetAtt&lt;/span&gt; &lt;span class="s"&gt;MyS3Bucket.WebsiteURL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  aws cloudformation update-stack &lt;span class="nt"&gt;--stack-name&lt;/span&gt; my-new-s3-stack &lt;span class="nt"&gt;--template-body&lt;/span&gt; file://s3_bucket_template.yaml &lt;span class="nt"&gt;--parameters&lt;/span&gt; &lt;span class="nv"&gt;ParameterKey&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;BucketName,ParameterValue&lt;span class="o"&gt;=&lt;/span&gt;your-unique-bucket-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure you monitor the progress of the stack update using the AWS Management Console. The stack update is complete when the stack status changes to &lt;strong&gt;&lt;code&gt;UPDATE_COMPLETE&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After the stack update is complete, the S3 bucket will have versioning enabled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deleting the Stack
&lt;/h2&gt;

&lt;p&gt;Deleting a CloudFormation stack means removing all the resources associated with the stack and the stack itself. &lt;/p&gt;

&lt;p&gt;&lt;br&gt;
  💡 &lt;strong&gt;&lt;em&gt;This is important because keeping unused resources running can increase your AWS costs. Deleting a stack helps you optimize costs by removing no longer required resources.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;



&lt;p&gt;Let’s go ahead and delete the &lt;code&gt;S3Stack&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  aws cloudformation delete-stack &lt;span class="nt"&gt;--stack-name&lt;/span&gt; my-new-s3-stack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;strong&gt;&lt;code&gt;my-new-s3-stack&lt;/code&gt;&lt;/strong&gt; with the name of the stack you want to delete. This command will initiate the deletion process, and AWS CloudFormation will start removing the resources associated with the stack.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note that the deletion process may take a few minutes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To check the progress of the stack deletion, you can use the AWS Management Console or the following AWS CLI command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws cloudformation describe-stacks &lt;span class="nt"&gt;--stack-name&lt;/span&gt; my-new-s3-stack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  I want to keep practicing…
&lt;/h2&gt;

&lt;p&gt;If you want to dive deeper, you must learn by creating more stacks and writing templates that actually do something. Here are some ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get Started with CloudFormation from the docs:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/GettingStarted.Walkthrough.html"&gt;Get started - AWS CloudFormation&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use AWS CloudFormation Designer to create a basic LAMP server:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/working-with-templates-cfn-designer-walkthrough-createbasicwebserver.html"&gt;Walkthrough: Use AWS CloudFormation Designer to create a basic web server - AWS CloudFormation&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Static website&lt;/strong&gt;: Create a simple static website hosted on Amazon S3 and distributed using Amazon CloudFront. Use CloudFormation to provision and configure the S3 bucket, CloudFront distribution, and Route53 records for custom domain setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless REST API&lt;/strong&gt;: Build a serverless REST API using AWS Lambda, Amazon API Gateway, and Amazon DynamoDB. Use CloudFormation to create and configure the necessary resources, including Lambda functions, API Gateway, and DynamoDB tables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalable web application&lt;/strong&gt;: Design a scalable web application using Amazon EC2 instances, Elastic Load Balancing, and Amazon RDS. Utilize CloudFormation to create and manage Auto Scaling groups, load balancers, and RDS instances.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Before you stop reading…
&lt;/h2&gt;

&lt;p&gt;Here is the full list of best practices to consider while working with CloudFormation: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html"&gt;AWS CloudFormation best practices - AWS CloudFormation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some of the practices that stood out for me based on my recent experience are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Use IAM to control access&lt;/em&gt;&lt;/strong&gt;: You should use IAM with AWS CloudFormation to specify what AWS CloudFormation actions users can perform, such as viewing stack templates, creating stacks, or deleting stacks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Do not embed credentials in your templates:&lt;/em&gt;&lt;/strong&gt; Rather than embedding sensitive information in your AWS CloudFormation templates, we recommend you use dynamic references in your stack template&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Manage all stack resources through AWS CloudFormation:&lt;/em&gt;&lt;/strong&gt; After you launch a stack, use the CloudFormation console, API, or AWS CLI to update resources in your stack. Don't make changes to stack resources outside of CloudFormation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Validate templates before using them:&lt;/em&gt;&lt;/strong&gt; Before you use a template to create or update a stack, you can use CloudFormation to validate it. For the AWS CLI or CloudFormation API, use the &lt;a href="https://docs.aws.amazon.com/cli/latest/reference/cloudformation/validate-template.html"&gt;aws cloudformation validate-template&lt;/a&gt; command or &lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_ValidateTemplate.html"&gt;ValidateTemplate&lt;/a&gt; operation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  So far, this is what I know about AWS CloudFormation…
&lt;/h2&gt;

&lt;p&gt;AWS CloudFormation is a powerful and versatile Infrastructure as Code tool that allows you to model, provision, and manage your AWS infrastructure efficiently. In this article, we delved into the basics of CloudFormation, explored its key components, and walked through a practical example of creating, updating, and deleting an S3 bucket using templates, parameters, and outputs.&lt;/p&gt;

&lt;p&gt;By leveraging AWS CloudFormation, you can enjoy the benefits of automation, consistency, version control, scalability, and cost savings when managing your cloud infrastructure. This article has provided a foundation for understanding AWS CloudFormation and inspired you to further explore and experiment with this tool.&lt;/p&gt;

</description>
      <category>awscloudformation</category>
      <category>aws</category>
      <category>infrastructureascode</category>
      <category>managingawsresources</category>
    </item>
    <item>
      <title>Getting Started with Infrastructure as Code (IaC): Terraform</title>
      <dc:creator>Samuel Umoren</dc:creator>
      <pubDate>Tue, 11 Apr 2023 23:27:35 +0000</pubDate>
      <link>https://dev.to/umoren/getting-started-with-infrastructure-as-code-iac-terraform-4mac</link>
      <guid>https://dev.to/umoren/getting-started-with-infrastructure-as-code-iac-terraform-4mac</guid>
      <description>&lt;p&gt;In my previous &lt;a href="https://dev.to/umoren/an-introduction-to-devops-principles-and-practices-14bl"&gt;article&lt;/a&gt;, I introduced Infrastructure as Code (IaC) as a key principle of DevOps and likened it to creating a recipe for setting up a computer system. To provide a more technical definition, HashiCorp, the company behind Terraform, describes IaC as infrastructure components (CPUs, memory, disk, firewalls, etc.) defined as code within definition files.&lt;/p&gt;

&lt;p&gt;As the backbone of the DevOps movement, IaC brings a host of benefits, including increased efficiency, consistency, and collaboration between DevOps teams. By adopting IaC, teams can automate the provisioning and management of infrastructure, making it easier to scale and adapt to changing needs.&lt;/p&gt;

&lt;p&gt;Infrastructure as Code (IaC) plays a critical role in DevOps by streamlining the process of managing and provisioning infrastructure. IaC allows for increased efficiency, collaboration, and automation by treating infrastructure like software code. Here are some key benefits of IaC in DevOps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automation&lt;/strong&gt;: With IaC, you can create reusable templates to automate the provisioning and management of your infrastructure. This reduces manual intervention and minimizes the risk of human error, ultimately leading to more reliable and consistent environments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Version control&lt;/strong&gt;: IaC allows you to store your infrastructure definitions in version control systems like Git, making it easier to track changes, roll back to previous configurations, and collaborate with team members.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consistency:&lt;/strong&gt; IaC ensures that your infrastructure is consistent across different environments, such as development, testing, and production. This consistency helps avoid configuration drift and makes identifying and fixing issues easier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scalability:&lt;/strong&gt; IaC simplifies scaling your infrastructure up or down as needed. You can easily replicate your infrastructure setup across multiple cloud regions or accounts, ensuring that your resources are available when and where they're needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost savings:&lt;/strong&gt; By automating repetitive tasks and reducing the risk of human error, IaC can help you save time and money. Additionally, IaC enables you to manage your cloud resources better and optimize costs by quickly spinning up or tearing down resources based on demand.&lt;/p&gt;

&lt;p&gt;In this article, I'll dive deeper into Infrastructure as Code, focusing on two widely-used IaC tools: Terraform and CloudFormation. I'll explore their features, similarities, and differences to help you decide which tool best suits your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform
&lt;/h2&gt;

&lt;p&gt;Terraform is an open-source Infrastructure as Code (IaC) tool that allows you to define, manage, and provision cloud infrastructure using a simple, human-readable language called HashiCorp Configuration Language (HCL). With Terraform, you can create a blueprint for your infrastructure that can be version-controlled, shared, and easily modified.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Declarative language: Terraform uses HCL, a declarative language that focuses on describing the desired end-state of your infrastructure. This makes it easy to understand and maintain, even for those new to IaC.&lt;/li&gt;
&lt;li&gt;Modularity: Terraform encourages the use of modules, which are reusable building blocks for your infrastructure. This allows you to create and share reusable templates, improving collaboration and reducing duplicate code.&lt;/li&gt;
&lt;li&gt;Plan &amp;amp; apply: Terraform's two-step process involves first generating an execution plan, which shows you the changes that will be made to your infrastructure, and then applying those changes if the plan is approved. This provides better visibility and control over your infrastructure changes.&lt;/li&gt;
&lt;li&gt;State management: Terraform maintains a state file that tracks the current state of your infrastructure. This enables Terraform to detect changes, track dependencies, and ensure consistency between your infrastructure definition and actual deployed resources.&lt;/li&gt;
&lt;li&gt;Providers: Providers in Terraform are plugins that enable communication with specific cloud platforms, services, or APIs. They define the resources, data sources, and input variables needed to manage infrastructure on a particular platform.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Terraform supports a wide range of cloud providers, including major players like AWS, Google Cloud Platform, and Azure, as well as smaller providers and on-premises solutions. The list of providers is on the &lt;a href="https://registry.terraform.io/browse/providers"&gt;Terraform Registry&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To better understand how and why Terraform is used, let’s dive into the Terraform Workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Terraform Workflow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Write the configuration: Define the desired infrastructure configuration in a Terraform file using a declarative language like HCL. This file specifies the resources you want to create, modify, or delete, along with their configuration settings.&lt;/li&gt;
&lt;li&gt;Initialize the backend: Run &lt;strong&gt;&lt;code&gt;terraform init&lt;/code&gt;&lt;/strong&gt; to initialize Terraform in your working directory, download necessary provider plugins, and set up the backend for storing your Terraform state. This step only needs to be performed once per Terraform project.&lt;/li&gt;
&lt;li&gt;Plan and apply changes: Run &lt;strong&gt;&lt;code&gt;terraform plan&lt;/code&gt;&lt;/strong&gt; to preview the actions that Terraform will take based on the defined configuration. This step generates an execution plan, showing you what actions Terraform will perform on your infrastructure to reach the desired state. Review the plan and if everything looks correct, run &lt;strong&gt;&lt;code&gt;terraform apply&lt;/code&gt;&lt;/strong&gt; to create or modify the infrastructure. Terraform will prompt you to confirm before executing the necessary API calls.&lt;/li&gt;
&lt;li&gt;Destroy resources: If the resources are no longer needed, run &lt;strong&gt;&lt;code&gt;terraform destroy&lt;/code&gt;&lt;/strong&gt; to remove them. Terraform will prompt you to confirm before deleting the resources, ensuring that you don't accidentally remove something you still need.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s implement this workflow with a real use case. &lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Single EC2 Instance on AWS with Terraform
&lt;/h2&gt;

&lt;p&gt;AWS EC2 (Amazon Web Services Elastic Compute Cloud) is a scalable cloud computing service that provides virtual server instances for running applications and workloads. An EC2 instance has variety of purposes, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hosting web applications and websites.&lt;/li&gt;
&lt;li&gt;Running databases and data processing tasks.&lt;/li&gt;
&lt;li&gt;Deploying and managing containerized applications.&lt;/li&gt;
&lt;li&gt;Executing high-performance computing workloads.&lt;/li&gt;
&lt;li&gt;Serving as a development and test environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Upon creation, the EC2 instance starts running in the specified virtual private cloud (VPC), and you'll be billed for the resources used according to AWS pricing. Let’s go ahead and use terraform to create an EC2 instance on AWS. &lt;/p&gt;

&lt;h3&gt;
  
  
  Pre-requisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;AWS Account: Sign up for an AWS account if you don't have one already.&lt;/li&gt;
&lt;li&gt;AWS CLI: Install and configure the AWS CLI on your local machine. For instructions, follow this guide: &lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html"&gt;https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Terraform: Install Terraform by following the official installation guide: &lt;strong&gt;&lt;a href="https://learn.hashicorp.com/tutorials/terraform/install-cli"&gt;https://learn.hashicorp.com/tutorials/terraform/install-cli&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Code editor (preferably VS Code)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The complete code is on Github.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up
&lt;/h3&gt;

&lt;p&gt;Create a new directory for the Terraform project and navigate to it in your terminal.&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;mkdir &lt;/span&gt;terraform-ec2-instance
&lt;span class="nb"&gt;cd &lt;/span&gt;terraform-ec2-instance
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, Create a file named &lt;code&gt;provider.tf&lt;/code&gt; &lt;strong&gt;i&lt;/strong&gt;n your working directory and add the following configuration to the &lt;strong&gt;&lt;code&gt;provider.tf&lt;/code&gt;&lt;/strong&gt; file to specify the AWS provider and the desired AWS region.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;provider &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  region &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find AWS regions far right of the top navbar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--79vrOljz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681255779/Screenshot_2023-04-11_at_22.44.02_dlozii.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--79vrOljz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681255779/Screenshot_2023-04-11_at_22.44.02_dlozii.png" alt="Screen" width="800" height="53"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing the configuration
&lt;/h3&gt;

&lt;p&gt;To launch an instance in AWS, you need an Amazon Machine Image (AMI). It is a supported and maintained image provided by AWS that provides the information required to launch an instance&lt;/p&gt;

&lt;p&gt;Create a file named &lt;strong&gt;&lt;code&gt;ec2_instance.tf&lt;/code&gt;&lt;/strong&gt; in your working directory and add the following configuration to the &lt;strong&gt;&lt;code&gt;ec2_instance.tf&lt;/code&gt;&lt;/strong&gt; file to define an EC2 instance resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;resource &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  ami           &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ami-092401efdf89a2db7"&lt;/span&gt;
  instance_type &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;

  tags &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    Name &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"new-test-instance"&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;Replace &lt;code&gt;ami-0c55b159cbfafe1f0&lt;/code&gt; with an Amazon Machine Image (AMI) ID for your desired Linux distribution and instance region.&lt;/p&gt;

&lt;p&gt;You can find an Amazon Linux 2 AMI ID from the EC2 dashboard &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html#finding-an-ami-console"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I chose this AMI because it’s not on the aws-marketplace so i don’t have to subscribe to use it. Your AMI list might vary based on the region you’re using.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K4690sEz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681255779/Screenshot_2023-04-12_at_00.03.01_aj5lyb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K4690sEz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681255779/Screenshot_2023-04-12_at_00.03.01_aj5lyb.png" alt="Screen" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will want to make sure that the Root device type is EBS (Elastic Block Store) and Virtualization type is hvm (Hardware Virtual Machine). EBS is a persistent block storage service designed for use with Amazon EC2 instances and HVM is a type of virtualization mode available for EC2 instances.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;t2.micro&lt;/code&gt; instance is in the general purpose family with 1 vCPUs, 1.0 GiB of memory and low to moderate network performance. In our case, this is perfect because we are not doing any network or memory intensive work. &lt;br&gt;
Navigate to your &lt;strong&gt;Instance&lt;/strong&gt; in your dashboard to see the list of available instance types.&lt;/p&gt;

&lt;p&gt;Also, ensure your IAM user has &lt;code&gt;AmazonEC2FullAccess&lt;/code&gt; permission allowed. &lt;/p&gt;
&lt;h3&gt;
  
  
  Initialize and apply the Terraform configuration
&lt;/h3&gt;

&lt;p&gt;Run &lt;strong&gt;&lt;code&gt;terraform init&lt;/code&gt;&lt;/strong&gt; in your terminal to initialize your Terraform working directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run &lt;strong&gt;&lt;code&gt;terraform validate&lt;/code&gt;&lt;/strong&gt; to validate your configuration files for any syntax errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform validate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, run &lt;strong&gt;&lt;code&gt;terraform apply&lt;/code&gt;&lt;/strong&gt; to create the EC2 instance. Terraform will prompt you to confirm that you want to perform the action. Type &lt;strong&gt;&lt;code&gt;yes&lt;/code&gt;&lt;/strong&gt; and press Enter to continue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything works fine, you should see something like this on your terminal&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--h3OOfTGF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681255779/Screenshot_2023-04-12_at_00.00.51_r6hb5y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--h3OOfTGF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681255779/Screenshot_2023-04-12_at_00.00.51_r6hb5y.png" alt="Screen" width="800" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Verifying the EC2 instance
&lt;/h3&gt;

&lt;p&gt;Head to your AWS Management Console and navigate to the EC2 Dashboard. You should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VkruTxBy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681255780/Screenshot_2023-04-12_at_00.12.41_ehg0ym.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VkruTxBy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681255780/Screenshot_2023-04-12_at_00.12.41_ehg0ym.png" alt="Screen" width="800" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on Instances running and you should see the instance we just created.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3QeuPd-r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681255779/Screenshot_2023-04-12_at_00.12.12_qpdqpa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3QeuPd-r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/sammy365/image/upload/v1681255779/Screenshot_2023-04-12_at_00.12.12_qpdqpa.png" alt="Screen" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice how the configuration matches with the &lt;code&gt;ec2_instance.tf&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clean up
&lt;/h3&gt;

&lt;p&gt;Now we have achieved our goal, let’s destroy the EC2 instance to avoid unnecessary costs.&lt;/p&gt;

&lt;p&gt;Run &lt;strong&gt;&lt;code&gt;terraform destroy&lt;/code&gt;&lt;/strong&gt; in your terminal. Terraform will prompt you to confirm that you want to perform the action. Type &lt;strong&gt;&lt;code&gt;yes&lt;/code&gt;&lt;/strong&gt; and press Enter to continue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After a few minutes, Terraform will destroy the EC2 instance. Ensure you check your dashboard to confirm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations of Terraform
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Learning curve: Users may face a learning curve when getting started with Terraform, particularly if they are new to Infrastructure as Code and cloud concepts.&lt;/li&gt;
&lt;li&gt;Provider limitations: While Terraform supports many cloud providers, certain features or services might not be available or fully supported due to limitations in the provider implementation.&lt;/li&gt;
&lt;li&gt;State management: Terraform relies on a state file to track the resources it manages. This state file can become a point of contention in team environments, requiring additional configuration and tooling, such as remote state backends and locking mechanisms.&lt;/li&gt;
&lt;li&gt;Error messages: Terraform can sometimes produce verbose or unclear error messages, making it challenging to diagnose and fix issues.&lt;/li&gt;
&lt;li&gt;Performance: Large infrastructure projects with numerous resources and modules can lead to slow performance during planning and applying changes.&lt;/li&gt;
&lt;li&gt;No built-in rollback: Terraform does not have a built-in rollback mechanism for reverting changes, which can make recovering from errors more difficult. Users must plan their rollback strategy by leveraging backups, snapshots, or version control systems.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  So far this is what i know on IaC and Terraform…
&lt;/h2&gt;

&lt;p&gt;IaC (Infrastructure as Code) is a key DevOps principle that streamlines infrastructure management and provisioning. It increases efficiency, collaboration, and automation by treating infrastructure as code. IaC offers several benefits, including automation, version control, consistency, scalability, and cost savings.&lt;/p&gt;

&lt;p&gt;Terraform, an open-source IaC tool, enables defining, managing, and provisioning cloud infrastructure using HCL (HashiCorp Configuration Language). It provides modularity, a plan &amp;amp; apply approach, and state management, working with a wide range of cloud providers.&lt;/p&gt;

&lt;p&gt;Terraform's workflow includes writing the configuration, initializing the backend, planning and applying changes, and destroying resources when needed. This simplifies infrastructure setup, encourages collaboration, and ensures consistency across environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Terraform Documentation: Official documentation and getting started guide for Terraform.
&lt;strong&gt;&lt;a href="https://www.terraform.io/docs/index.html"&gt;https://www.terraform.io/docs/index.html&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;AWS CloudFormation Documentation: Official documentation and getting started guide for AWS CloudFormation.
&lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/cloudformation/index.html"&gt;https://docs.aws.amazon.com/cloudformation/index.html&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Gruntwork Blog - Terraform vs. CloudFormation: A comparison of the two IaC tools, discussing their strengths and weaknesses.
&lt;strong&gt;&lt;a href="https://blog.gruntwork.io/terraform-vs-cloudformation-f3809ea9ac3a"&gt;https://blog.gruntwork.io/terraform-vs-cloudformation-f3809ea9ac3a&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Medium - Terraform vs. AWS CloudFormation: A detailed comparison of Terraform and AWS CloudFormation, focusing on use cases and examples.
&lt;strong&gt;&lt;a href="https://medium.com/@codeometry/terraform-vs-aws-cloudformation-1d9716122623"&gt;https://medium.com/@codeometry/terraform-vs-aws-cloudformation-1d9716122623&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;What is AWS CloudFormation.
&lt;a href="https://www.youtube.com/watch?v=0Sh9OySCyb4"&gt;https://www.youtube.com/watch?v=0Sh9OySCyb4&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;*&lt;strong&gt;&lt;em&gt;AWS Cloudformation Step by Step Tutorial - Create a DynamoDB Table!&lt;/em&gt;&lt;/strong&gt;*
&lt;a href="https://www.youtube.com/watch?v=YXVCdGyHDSk"&gt;https://www.youtube.com/watch?v=YXVCdGyHDSk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;*&lt;strong&gt;&lt;em&gt;Terraform Course - Automate your AWS cloud infrastructure.&lt;/em&gt;&lt;/strong&gt;*
&lt;a href="https://www.youtube.com/watch?v=SLB_c_ayRMo"&gt;https://www.youtube.com/watch?v=SLB_c_ayRMo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;*&lt;strong&gt;&lt;em&gt;Terraform explained in 15 mins | Terraform Tutorial for Beginners.&lt;/em&gt;&lt;/strong&gt;*
&lt;a href="https://www.youtube.com/watch?v=l5k1ai_GBDE"&gt;https://www.youtube.com/watch?v=l5k1ai_GBDE&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;HashiCorp Learn - Introduction to Terraform: A comprehensive resource for learning Terraform, including tutorials, best practices, and use cases.
&lt;strong&gt;&lt;a href="https://learn.hashicorp.com/terraform"&gt;https://learn.hashicorp.com/terraform&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;AWS CloudFormation Best Practices: Official best practices guide for using AWS CloudFormation.
&lt;strong&gt;&lt;a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html"&gt;https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/best-practices.html&lt;/a&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>terraform</category>
      <category>cloud</category>
      <category>devops</category>
      <category>iac</category>
    </item>
    <item>
      <title>Turbopack: The successor to Webpack</title>
      <dc:creator>Samuel Umoren</dc:creator>
      <pubDate>Sun, 09 Apr 2023 12:02:31 +0000</pubDate>
      <link>https://dev.to/umoren/turbopack-the-successor-to-webpack-284j</link>
      <guid>https://dev.to/umoren/turbopack-the-successor-to-webpack-284j</guid>
      <description>&lt;p&gt;Once upon a time in web development, building web pages required just HTML, CSS, and some JavaScript for interactivity. This was the typical code structure of most websites:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fsammy365%2Fimage%2Fupload%2Fv1681041577%2FUntitled_gz350c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fsammy365%2Fimage%2Fupload%2Fv1681041577%2FUntitled_gz350c.png" alt="code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most often, the developers will introduce third-party JS libraries for extra interactivity. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fsammy365%2Fimage%2Fupload%2Fv1681041577%2FUntitled_1_g0ezcu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fsammy365%2Fimage%2Fupload%2Fv1681041577%2FUntitled_1_g0ezcu.png" alt="html code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;JavaScript was perceived as a client-side scripting language and nothing more during this period. That would soon change, especially with the introduction of Nodejs. Node proved that JavaScript had more client-side capabilities, such as handling network requests, routing, complex animations, and storage. &lt;/p&gt;

&lt;p&gt;Then we started building large-scale products like eCommerce sites, social media apps, learning platforms, e.t.c, with JavaScript. This introduced more third-party libraries, and CDN (content delivery networks) weren’t popular then, so you had to download the library JS files. This made code so messy and difficult to maintain, and the developer experience was horrible. &lt;/p&gt;

&lt;p&gt;During this period, the ever-progressive JS community started focusing on improving developer experience with developer tools. This led to the birth of &lt;strong&gt;Bundlers.&lt;/strong&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Bundlers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A bundler is simply a development tool aggregating all JS files as input and outputs a single JS file that is loadable on a web browser. A bundler ensures that all source code and third-party dependencies are up-to-date and error-free. Before the era of bundling code, optimization and logging were major issues, a bundler solves that with features such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code splitting&lt;/li&gt;
&lt;li&gt;Hot module replacement (HMR)&lt;/li&gt;
&lt;li&gt;Loggers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So how do Bundlers work behind the scenes? &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dependency Resolution: The bundler generates a dependency graph of all served files.&lt;/li&gt;
&lt;li&gt;Bundling: The bundler outputs static assets that the browser can parse. This process is referred to as &lt;strong&gt;Packing.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bundlers like browserify have existed since 2010 using &lt;code&gt;require&lt;/code&gt; to load npm packages in the browser. However, JavaScript didn’t have a built-in module system until 2015, with the introduction of ES6. This new wave of modular programming led to the birth of module bundlers like webpack, rollup, parcel, and esbuild. Of all the bundlers, Webpack got everyone’s attention and is currently the most used bundler with ~26million weekly downloads. &lt;/p&gt;

&lt;h3&gt;
  
  
  Webpack
&lt;/h3&gt;

&lt;p&gt;Webpack is a &lt;em&gt;static module bundler&lt;/em&gt;. When introduced to a project, it generates a dependency graph from one entry point (more like &lt;code&gt;index.js&lt;/code&gt;) or more, and it combines all the modules (JS and non-JS) into one or more (depending on your configuration) bundles. These bundles become static files (html, css, js, assets) that the browser can process. It requires no configuration to bundle your project but is very configurable. &lt;/p&gt;

&lt;p&gt;Let’s briefly take a look at how webpack approaches dependency resolution. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Entry Point&lt;/strong&gt;: This is where webpack starts when building its internal dependency graph. By default, it is &lt;code&gt;index.js&lt;/code&gt;, but you can choose a different entry point or more than one point.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;webpack.config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;entry&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="s1"&gt;../../index.js&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="s1"&gt;../../server.js&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;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Output&lt;/strong&gt;: This is the location of the bundles. By default, the output property creates a &lt;code&gt;dist&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;webpack.config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dist&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;/li&gt;
&lt;li&gt;
&lt;p&gt;***********&lt;strong&gt;&lt;em&gt;Loaders&lt;/em&gt;&lt;/strong&gt;***********: Ever wondered how webpack parses assets such as HTML, CSS and media files? it uses Loaders. Loaders convert these files into consumable modules and add them to the dependency graph.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;webpack.config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;module&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;rules&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="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.(&lt;/span&gt;&lt;span class="sr"&gt;js|jsx&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/node-modules/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;babel-loader&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="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;html$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;html-loader&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="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.(&lt;/span&gt;&lt;span class="sr"&gt;scss|sass&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;use&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;style-loader&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;css-loader&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;sass-loader&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="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.(&lt;/span&gt;&lt;span class="sr"&gt;png|jpe&lt;/span&gt;&lt;span class="se"&gt;?&lt;/span&gt;&lt;span class="sr"&gt;g|gif&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;use&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="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file-loader&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="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Plugins:&lt;/strong&gt; One of the major reasons webpack is loved by all is its plugin system. Webpack plugins allow you to perform tasks such as bundle optimization, asset management, and injection of environment variables.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;webpack.config.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BrotliPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;brotli-webpack-plugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BrotliPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[path].br[query]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.(&lt;/span&gt;&lt;span class="sr"&gt;js|css|html|svg&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Mode:&lt;/strong&gt; By setting mode, you choose the environment webpack optimizes for.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&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;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the core concepts of webpack. If you’ve worked with JavaScript frameworks such as Vue, React, Angular, and so on, you’ll notice these core webpack concepts are implemented. This is because these frameworks use Webpack for bundling. &lt;/p&gt;

&lt;p&gt;The impact of webpack in frontend tooling and architecture is vast. It's widely used in Single Page Applications (SPA), Server-Side Rendering Applications, and Static Site Generators (SSGs). In short, other language frameworks, such as PHP (Laravel) and Ruby(Rails), use webpack to manage JavaScript, CSS, and static assets like images or fonts.&lt;/p&gt;

&lt;p&gt;Moreover, with the availability of native ES modules in the browser and the rise of JavaScript tools written in compile-to-native languages, the choice of bundler requires more attention. Webpack has issues such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Slow development server: The Webpack plugin system is one of its biggest pros and cons. The heavy reliance on plugins to perform certain tasks can slow down the bundler and hence increase the time it takes to start the development server.&lt;/p&gt;

&lt;p&gt;Also, the entire application is rebuilt every time you make changes to a file. Imagine what happens in larger projects.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Complexity: As a project grows and more plugins are introduced, the configuration becomes more complex.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It generates code that is impossible to read. This is always a problem, especially when refactoring code in a large project.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These issues led the ever-progressive JS community to develop better alternatives to Webpack. One of the most successful alternatives so far is Vite. Let’s briefly explore Vite. &lt;/p&gt;

&lt;h2&gt;
  
  
  Vite
&lt;/h2&gt;

&lt;p&gt;Vite is a &lt;strong&gt;build tool&lt;/strong&gt; that provides a faster development experience in web projects. Unlike bundlers, Vite consists of two parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rollup for code bundling&lt;/li&gt;
&lt;li&gt;A dev server with extensive features, including fast Hot Module Replacement (HMR)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The HMR API in Vite is considerably faster than Webpack. Vite solves the slow development server problem we had with Webpack, even as the project expands. Vite ushers in a new era of ESM-based development tools and Bundleless architecture. We won’t go deep into Vite cause it’s past the scope of this article, but the goal of introducing it is so you’ll note these points;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vite is a build tool, not a bundler. It’s faster, and most teams are migrating from webpack to Vite.&lt;/li&gt;
&lt;li&gt;Vite is not a direct replacement for Webpack. It’s just better than webpack in most cases.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These points raise the question, “What’s the successor to Webpack?”. A few weeks ago, Vercel answered that question with the release of Turbopack, and in this article, we’ll learn about what Turbopack is, its pros and cons, how to use it on a Nextjs project, what makes it better than Webpack, and we’ll briefly discuss the future of webpack-based projects. &lt;/p&gt;

&lt;h2&gt;
  
  
  Turbopack
&lt;/h2&gt;

&lt;p&gt;Turbopack is an incremental bundler optimized for your JavaScript and Typescript projects. Unlike other bundlers written in JS/TS, Turbopack is Rust based. It’s the official successor to Webpack, and the creators of Webpack and Nextjs are building it. &lt;/p&gt;

&lt;p&gt;Turbopack claims to be 700x faster than Webpack and 10x faster than Vite (Vite creator disagrees with this) in large projects. &lt;/p&gt;

&lt;p&gt;So what makes Turbopack so fast? &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compilation by Request: We’ve discussed the importance of startup time in developer experience and how the Webpack dev server takes seconds/minutes to start as the project gets bigger because it rebuilds the entire application every time a file is changed. Turbopack, on the other hand, compiles only the code needed to start the project.&lt;/li&gt;
&lt;li&gt;Turbo Engine: Turbo Engine is a powerful Rust library that enables incremental computation. In computer science, the incremental computation approach is when a sequence of inputs is slightly different from each other, it uses the previously computed output in computing a new output rather than computing the new output from scratch. This is applied in optimizing compilers, and one way to achieve incremental computation is through caching. Turbo Engine implements incremental computation using Function-level caching.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s explore the Features and Cons of Turbopack. &lt;/p&gt;

&lt;h3&gt;
  
  
  Features and Cons of TurboPack
&lt;/h3&gt;

&lt;p&gt;Let’s highlight some of the features of Turbopack &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster development server time: Turbopack supports Hot Module Replacement out of the box, and its HMR is way faster due to incremental computation. HMR ensures your dev server doesn’t fully refresh after every file change.&lt;/li&gt;
&lt;li&gt;Out-of-the-box support for JS &amp;amp; TS: Turbopack bundles JavaScript and Typescript but not with Babel. Turbopack uses a Rust-based compilation tool, SWC(Speedy Web Compiler). For context, SWC claims to be 17x faster than Babel.&lt;/li&gt;
&lt;li&gt;Out-of-the-box support for CJS and ESM imports: Whatever method you use to import modules, dynamic imports, ES Modules, or CommonJS, it’s supported by Turbopack.&lt;/li&gt;
&lt;li&gt;Live reloading for Environmental variables: One of the most annoying experiences when developing is having to close and reload your server after changing environmental variables. Turbopack ships with live reloading for environmental variables.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s highlight some cons of Turbopack so far. It’s important to note that Turbopack is still very new and experimental, so these issues might be fixed as it matures. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lacks god-level Extensibility: Webpack had what I refer to as god-level extensibility with its plugin API. Turbopack doesn’t support plugins, but they promise that the bundler will be extensible in future versions. However, they won’t be porting the Webpack plugin API, meaning most Webpack plugins you enjoy today won’t work with Turbopack.&lt;/li&gt;
&lt;li&gt;Does not perform Type Checks: Turbopack uses SWC for compiling TS and doesn’t have out-of-the-box support for type checking. Like Vite and esbuild, with Turbopack, you must run &lt;code&gt;tsc --watch&lt;/code&gt; or depend on your code IDE for type checking.&lt;/li&gt;
&lt;li&gt;Only supports the Nextjs dev server.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to use Turbopack
&lt;/h3&gt;

&lt;p&gt;Turbopack is only at its Alpha version and has only been deployed as a Nextjs v13 dev server. To run a Nextjs v13 project powered by Turbopack, run this command on your terminal. &lt;/p&gt;

&lt;p&gt;💡 Turbopack for Next.js does not require loaders nor loader configuration for built-in functionality, just as they aren't required for Next.js. Turbopack has built-in support for css and compiling modern JavaScript, so there's no need for &lt;code&gt;css-loader&lt;/code&gt;, &lt;code&gt;postcss-loader&lt;/code&gt;, or &lt;code&gt;babel-loader&lt;/code&gt; if you're using &lt;code&gt;@babel/preset-env&lt;/code&gt;.&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app &lt;span class="nt"&gt;--example&lt;/span&gt; with-turbopack
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will bootstrap a Nextjs v13 sample with React Server components. Run &lt;code&gt;yarn install&lt;/code&gt; to install dependencies. &lt;/p&gt;

&lt;p&gt;Now for the moment of truth. Let’s run the project with &lt;code&gt;yarn dev&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fsammy365%2Fimage%2Fupload%2Fv1681041579%2FScreenshot_2022-11-02_at_21.34.44_awjrrx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fsammy365%2Fimage%2Fupload%2Fv1681041579%2FScreenshot_2022-11-02_at_21.34.44_awjrrx.png" alt="Screenshot 2022-11-02 at 21.34.44.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;6.24ms! with 20+ components!&lt;/p&gt;

&lt;p&gt;For context, let’s compare this startup time with a non-turbopack Nextjs v13 project with fewer components and dependencies. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fsammy365%2Fimage%2Fupload%2Fv1681041577%2FScreenshot_2022-11-02_at_21.32.46_yw7jno.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fsammy365%2Fimage%2Fupload%2Fv1681041577%2FScreenshot_2022-11-02_at_21.32.46_yw7jno.png" alt="Screenshot 2022-11-02 at 21.32.46.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compiled client and server in 11s! &lt;/p&gt;

&lt;p&gt;The difference is clear. We can only look forward to when Turbopack will be a low-level engine for other frameworks. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Migrating from Webpack&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As we’ve discussed, Turbopack is still in experiment mode and is not yet ready for production environments. So at the moment, you can’t port your project to Turbopack. &lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for Webpack users
&lt;/h2&gt;

&lt;p&gt;Firstly, to webpack aficionados, Turbopack is a preview of the future. Webpack has ~26m downloads weekly, which will continue for as long as possible until the project maintainers pull off the plug. &lt;/p&gt;

&lt;p&gt;If you visit Snowpack (not maintained anymore), you will be advised to use Vite. The same thing will happen with Webpack and Turbopack in the future.  &lt;/p&gt;

&lt;p&gt;However, Turbopack is managed by Vercel, and we don’t know when the bundler will be ready for wide adoption. I recommend Vite if you’re looking for an alternative to Webpack. &lt;/p&gt;

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

&lt;p&gt;Turbopack is a promising project that will redefine bundling tools architecture in an era where build tools such as Vite and esbuild are replacing bundlers. In this article, we learned what Bundlers are and how they work. Then we introduced Webpack as the bundler par excellence tool; we went on to learn its core concepts and briefly explored Vite as an alternative to webpack. Furthermore, we discovered Vite is a build tool and not the successor to Webpack. This introduced us to Turbopack. We learned what Turbopack was, how it worked, its top features and issues, how to use it in a project, and how it affects existing Webpack users.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>nextjs</category>
      <category>developer</category>
    </item>
    <item>
      <title>An Introduction to DevOps: Principles and Practices</title>
      <dc:creator>Samuel Umoren</dc:creator>
      <pubDate>Sat, 08 Apr 2023 20:47:49 +0000</pubDate>
      <link>https://dev.to/umoren/an-introduction-to-devops-principles-and-practices-14bl</link>
      <guid>https://dev.to/umoren/an-introduction-to-devops-principles-and-practices-14bl</guid>
      <description>&lt;p&gt;A few weeks ago, I was on a community call with some front-end engineers, and I noticed how well they were versed in DevOps and Infrastructure. I spent half of the time nodding my head, and whenever I heard “Continuous integration or Continuous deployment or Git”, I would scream, “Yeahhhh,” not cause I really know them, but I remember a few years ago when you had to manually setup GitHub Actions for your Netlify deployments, now it’s automatic. After the meeting, I promised myself I would learn the basics of DevOps and Infrastructure. &lt;/p&gt;

&lt;p&gt;My favorite learning method is reading and writing articles about it. This is how I’ll educate myself on DevOps and Infra.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is DevOps?
&lt;/h2&gt;

&lt;p&gt;DevOps is a combination of the words "development" and "operations." It's a set of practices and principles that aims to bridge the gap between software development and IT operations teams. By promoting collaboration, communication, and integration between these teams that have traditionally worked separately, DevOps enables organizations to build, test, and deploy software more quickly and reliably. The main goal of DevOps is to streamline the entire software development lifecycle, from planning to deployment and beyond.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Ways of DevOps
&lt;/h2&gt;

&lt;p&gt;The Three Ways of DevOps are the guiding principles that form the foundation of DevOps practices. These principles help teams to understand the core values of DevOps better and provide a framework for implementing its practices effectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Flow is all about creating smooth, efficient, and continuous processes throughout the software development lifecycle. Imagine a river flowing from the source to the destination without any obstacles or interruptions. In the context of DevOps, the "river" represents the process of delivering software changes from development to operations and finally to the end-users. &lt;/p&gt;

&lt;p&gt;To improve flow, teams should focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removing bottlenecks: Like rocks in a river, bottlenecks slow down the flow of work. Identify and eliminate them to increase the speed of software delivery.&lt;/li&gt;
&lt;li&gt;Minimizing handoffs: When work is passed from one team to another, like passing a baton in a relay race, it can create delays and miscommunication. Reducing these handoffs can streamline the process.&lt;/li&gt;
&lt;li&gt;Automating repetitive tasks: Automation can help to speed up processes by performing tasks quickly and consistently, just like a machine on an assembly line.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Feedback is about creating a closed-loop system where information flows back from operations to development, similar to a two-way street. This feedback allows teams to learn from their mistakes, identify areas for improvement, and respond to customer needs quickly.&lt;/p&gt;

&lt;p&gt;In the DevOps context, feedback can be achieved through the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitoring: Keep an eye on the performance and health of your applications and infrastructure, like checking the dashboard of a car.&lt;/li&gt;
&lt;li&gt;Incident reports: When issues occur, document and share them with the development team to prevent similar problems in the future.&lt;/li&gt;
&lt;li&gt;Customer feedback: Listening to end-users and making improvements based on their needs and experiences.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Continuous Learning and Experimentation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Continuous learning and experimentation encourage teams to refine their processes, tools, and techniques constantly. Think of it as planting a garden: by regularly tending to it, learning from past mistakes, and trying new approaches, you can create a thriving and productive environment.&lt;/p&gt;

&lt;p&gt;In a DevOps setting, continuous learning and experimentation involve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learning from failures: When things go wrong, treat them as learning opportunities and use the knowledge gained to improve your processes.&lt;/li&gt;
&lt;li&gt;Embracing change: Be open to new ideas, tools, and technologies that can help your team work more efficiently and deliver better results.&lt;/li&gt;
&lt;li&gt;Experimenting: Test new approaches, measure their impact, and use the results to inform future decisions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Other DevOps Principles
&lt;/h2&gt;

&lt;p&gt;Some other core DevOps principles I learned of are:&lt;/p&gt;

&lt;h3&gt;
  
  
  Infrastructure as Code (IAC)
&lt;/h3&gt;

&lt;p&gt;Infrastructure as Code (IaC) is like creating a recipe for setting up a computer system. Instead of manually setting up servers, networks, and storage, you write a set of instructions in code. This "recipe" tells a computer how to set everything up automatically. With IaC, you can easily create and change your system's design, like editing a recipe. This approach saves time, reduces mistakes, and keeps your system consistent as if you were using the same recipe for cooking a dish multiple times. IaC is an essential part of DevOps that helps teams work faster and smarter. Some IaC tools that i hear of from Infra engineers frequently are Terraform, AWS CloudFormation, Ansible, Chef, and Puppet. These tools are essential for automating the management and provisioning of infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Continuous Improvement
&lt;/h3&gt;

&lt;p&gt;Have you met DevOps or Infra engineers? They are always trying to be ahead of the world when it comes to improving and refining their projects. Like maintaining a tidy room, it involves regularly reviewing performance, analyzing data, making changes, learning from mistakes, and adapting to new situations. By staying open to new ideas and constantly improving, teams can ensure their processes are efficient and effective, delivering the best possible results in software development and IT operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  DevOps Practices
&lt;/h2&gt;

&lt;p&gt;These practices are the methods and techniques used to implement DevOps principles in software development. The aim is to improve collaboration, streamline processes, and enhance the overall efficiency of the software development lifecycle. I’ll outline a few because I will dive deep into most of them in future articles.  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Continuous Integration (CI)&lt;/strong&gt;: Integrating code changes frequently and automatically testing them to catch issues early.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous Delivery (CD)&lt;/strong&gt;: Ensuring software changes are always in a releasable state, allowing faster and more reliable deployments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous Deployment&lt;/strong&gt;: Automatically deploy software changes to production, without manual intervention, once they pass required tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure as Code (IaC)&lt;/strong&gt;: Managing and provisioning infrastructure using code and automation tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring and Logging&lt;/strong&gt;: Collecting and analyzing data on application performance and infrastructure health to enable informed decision-making and proactive issue resolution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated Testing&lt;/strong&gt;: Writing and running automated tests to ensure code quality and reduce the risk of introducing errors during deployment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version Control&lt;/strong&gt;: Using tools like Git to manage and track changes to code, making collaboration and rollback easier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Microservices Architecture&lt;/strong&gt;: Designing applications as small, independent services that communicate through APIs, improving scalability and maintainability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration Management&lt;/strong&gt;: Automating the process of applying and maintaining the desired state of software components and infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Containerization and Orchestration&lt;/strong&gt;: Containerization puts software and its parts into separate, small boxes called containers, making them work the same everywhere. Orchestration helps control these containers automatically, making big systems easier to handle. Together, they make app development faster and simpler, allowing better use of DevOps resources.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Popular DevOps Tools
&lt;/h2&gt;

&lt;p&gt;I’ve seen a lot of DevOps tools, and I'm just 2 days into this DevOps journey, but these four stand out for me.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Git (Version Control):&lt;/strong&gt; In DevOps, Git helps track code changes and fosters collaboration between developers and operations teams. It streamlines the development process, allowing for easy rollback and merging of changes, improving the overall software delivery pipeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jenkins (CI/CD):&lt;/strong&gt; Jenkins plays a key role in DevOps by automating the Continuous Integration and Continuous Delivery processes. It ensures code quality and rapid deployment, catching errors early and facilitating seamless collaboration between development and operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker (Containerization):&lt;/strong&gt; Docker enhances DevOps workflows by packaging software into portable containers, ensuring consistent performance across environments. It simplifies development and deployment, enabling faster and more reliable delivery of applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes (Container Orchestration):&lt;/strong&gt; In DevOps, Kubernetes efficiently orchestrates containers, automating their deployment, scaling, and management. It optimizes resource utilization and supports complex, large-scale systems, improving the collaboration between development and operations teams.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  So far, this is what I know about DevOps…
&lt;/h2&gt;

&lt;p&gt;DevOps brings together developers and IT operations to create better software faster. It's like a team sport where everyone works together.&lt;/p&gt;

&lt;p&gt;The Three Ways of DevOps are like a playbook: Flow makes things move smoothly, Feedback is a two-way street for learning, and Continuous Learning encourages us to try new things. Other important ideas in DevOps are Infrastructure as Code (like a recipe for computer systems) and Continuous Improvement (always getting better).&lt;/p&gt;

&lt;p&gt;DevOps practices help teams work together, like using containers and orchestrating them to make app development easier. Popular tools in DevOps include Git for tracking code changes, Jenkins for automating testing and deployment, Docker for packaging software, and Kubernetes for managing containers.&lt;/p&gt;

&lt;p&gt;Next, &lt;strong&gt;&lt;a href="https://dev.to/umoren/getting-started-with-infrastructure-as-code-iac-terraform-4mac"&gt;Getting Started with Infrastructure as Code (IAC): Terraform vs. CloudFormation&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;I’m reading this book: &lt;strong&gt;The DevOps Handbook: How to Create World-Class Agility, Reliability, and Security in Technology Organizations&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://aws.amazon.com/devops/what-is-devops/"&gt;What is DevOps?&lt;/a&gt;&lt;/strong&gt; - Amazon Web Services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-devops"&gt;An Introduction to DevOps&lt;/a&gt;&lt;/strong&gt; - DigitalOcean&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://dzone.com/articles/the-top-5-devops-principles-to-know"&gt;The Top 5 DevOps Principles to Know&lt;/a&gt;&lt;/strong&gt; - DZone&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://stackify.com/top-devops-tools/"&gt;The Top 11 DevOps Tools for 2021&lt;/a&gt;&lt;/strong&gt; - Stackify&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.atlassian.com/continuous-delivery/principles/continuous-integration-vs-delivery-vs-deployment"&gt;Continuous Integration vs. Continuous Delivery vs. Continuous Deployment&lt;/a&gt;&lt;/strong&gt; - Atlassian&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://spacelift.io/blog/devops-best-practices"&gt;16 DevOps Best Practices Every Developer Should Know&lt;/a&gt;&lt;/strong&gt; - spacelift&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>devops</category>
      <category>infrastructure</category>
      <category>containers</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Migrating from Monolithic E-commerce Platforms to MedusaJS: A Guide for Team Leads and Project managers</title>
      <dc:creator>Samuel Umoren</dc:creator>
      <pubDate>Tue, 04 Apr 2023 00:40:34 +0000</pubDate>
      <link>https://dev.to/umoren/migrating-from-monolithic-e-commerce-platforms-to-medusajs-a-guide-for-team-leads-and-project-managers-1d22</link>
      <guid>https://dev.to/umoren/migrating-from-monolithic-e-commerce-platforms-to-medusajs-a-guide-for-team-leads-and-project-managers-1d22</guid>
      <description>&lt;p&gt;Developers building e-commerce solutions are familiar with popular monolithic e-commerce platforms such as Magneto, WooCommerce, and Oracle. These platforms are famous for being easy to use, their built-in features, faster time to market (arguably), and their established community of developers and users. This is why most developers would downplay the idea of migrating to a non-monolithic platform. To some extent, preferring to stick to monolithic platforms could be justified — if it works, don’t change it, right? &lt;/p&gt;

&lt;p&gt;However, as online shopping grows, businesses need more flexibility to offer personalized experiences and quickly adapt to changing customer needs. The reasons for this are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increased competition: As the number of online businesses continues to grow, customers have more options to choose from.&lt;/li&gt;
&lt;li&gt;Changing customer expectations: Customer expectations are constantly evolving, and businesses need to be able to adapt quickly to meet these changing needs.&lt;/li&gt;
&lt;li&gt;Higher customer engagement: By offering personalized experiences, businesses can increase customer engagement and loyalty, leading to higher conversion rates and repeat business.&lt;/li&gt;
&lt;li&gt;Improved customer satisfaction: Personalized experiences that cater to each customer's specific needs and preferences can lead to higher customer satisfaction levels, resulting in positive word-of-mouth and increased brand reputation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Is this where I introduce a headless e-commerce platform like Medusajs? Not yet. If you’re a project manager or team lead, you would be curious as to why you should consider not using a monolithic platform. Here are some common limitations of these platforms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Limited Flexibility&lt;/strong&gt;: Monolithic e-commerce platforms often have limited flexibility when it comes to customizing and extending functionality. This can make it difficult for businesses to adapt quickly to changing customer needs or to offer personalized experiences.&lt;/li&gt;
&lt;li&gt;Vendor Lock-In: Many monolithic e-commerce platforms are designed to keep users within their ecosystem, which can make it difficult to migrate to a different platform in the future.&lt;/li&gt;
&lt;li&gt;Limited Innovation: Monolithic e-commerce platforms may not always be able to keep up with the latest trends and innovations in e-commerce. This can limit a business's ability to offer cutting-edge features and functionality to its customers. In most cases, you will have to request and wait for your vendor to implement a new feature that suits your business/client needs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this short guide, I’ll discuss the benefits and challenges of migrating from a monolithic e-commerce platform to MedusaJS and how to create a seamless migration plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to Consider when choosing an E-commerce platform in 2023
&lt;/h3&gt;

&lt;p&gt;When choosing an e-commerce platform for your team or client, consider factors such as business requirements, scalability, customization, security, integration, support, and cost. By carefully evaluating these factors, you can choose an e-commerce platform that meets your specific needs and goals.&lt;/p&gt;

&lt;p&gt;This is where a headless e-commerce platform like Medusajs comes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits of Migrating to a Headless Commerce System:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Increased Flexibility&lt;/strong&gt;: MedusaJS, as a headless e-commerce platform, separates the front-end user interface from the back-end business logic, allowing for greater flexibility and agility in designing and implementing custom features and functionality.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Improved Performance:&lt;/strong&gt; By using a headless architecture, your developer teams can reduce the number of requests sent to the server, resulting in faster page load times and improved performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easier Integrations&lt;/strong&gt;: MedusaJS is built with APIs, making integrating other systems and platforms easier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better Customer Experience&lt;/strong&gt;: With a headless architecture, your team can offer a more personalized and engaging customer experience by tailoring the front-end user interface to each customer's specific needs and preferences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vendor Neutrality&lt;/strong&gt;: Headless e-commerce systems like Medusa are vendor-neutral platforms, meaning businesses are not locked into a specific vendor or technology stack. This can provide greater flexibility and cost savings over time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open Source&lt;/strong&gt;: MedusaJS is an open-source platform, which means that your team can access the source code and make modifications as needed by contributing to the project. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, migrating to a headless e-commerce system does not come without challenges. &lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges of Migrating to a Headless Commerce System:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Technical Complexity:&lt;/strong&gt; Migrating to a headless commerce system requires a certain level of technical expertise and may be challenging for organizations that lack the necessary skills and resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Legacy System Integration&lt;/strong&gt;: Integrating with existing legacy systems can be a challenge and may require significant effort to ensure compatibility and maintain data integrity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customization and Maintenance&lt;/strong&gt;: Customizing and maintaining a headless commerce system may require a significant investment in resources and ongoing maintenance.&lt;/li&gt;
&lt;li&gt;Community Support: Some headless commerce systems, like MedusaJS, may have a smaller community of users and developers compared to more established platforms. This can make it difficult to find support and resources when needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Having evaluated the pros and cons of migrating to a headless commerce system and making an educated decision to move on with it, you’ll have to create a migration plan and roadmap. &lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Migration Plan and Roadmap
&lt;/h2&gt;

&lt;p&gt;Designing a roadmap for migrating an e-commerce system requires a combined effort from all stakeholders in the team, especially the developers. Thus, the migration plan depends on the team’s goals and business requirements. However, they are core points you should consider when creating the roadmap, such as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Evaluate the current e-commerce platform and identify pain points and areas for improvement.&lt;/li&gt;
&lt;li&gt;Determine the scope of the migration and create a timeline for each phase of the project.&lt;/li&gt;
&lt;li&gt;Identify the resources required for the migration, including team members, software tools, and third-party services.&lt;/li&gt;
&lt;li&gt;Develop a testing and validation plan to ensure the new system meets business requirements.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Wrapping up…
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Plan and prepare&lt;/strong&gt;: Migrating to a headless commerce system requires careful planning and preparation. Ensure that you have the right team and resources in place before embarking on the migration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test and validate:&lt;/strong&gt; Testing and validation are critical to ensuring the new system meets your business requirements. Test early and often, and involve stakeholders in the process to ensure everyone is on the same page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Address legacy system integration early:&lt;/strong&gt; Integrating with existing legacy systems can be a challenge and may require significant effort to ensure compatibility and maintain data integrity. Address this issue early in the migration process to avoid delays and complications later.&lt;/p&gt;

&lt;p&gt;Migrating from a monolithic e-commerce platform to a headless commerce system like MedusaJS offers significant benefits in terms of flexibility, performance, and customer experience. However, it requires careful planning, preparation, ongoing maintenance, and customization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.medusajs.com/"&gt;MedusaJS Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.shopify.com/enterprise/headless-commerce"&gt;Headless Commerce Explained: A Guide to Headless E-commerce Architecture&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://reuben09.hashnode.dev/building-an-e-commerce-store-a-step-by-step-guide-with-solidjs-and-medusa"&gt;Building an E-commerce Store: A Step-by-Step Guide with Solidjs and Medusa&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>product</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Designing Scalable and High-performance E-commerce Systems with MedusaJS and Microservices Architecture</title>
      <dc:creator>Samuel Umoren</dc:creator>
      <pubDate>Mon, 03 Apr 2023 19:20:56 +0000</pubDate>
      <link>https://dev.to/umoren/designing-scalable-and-high-performance-e-commerce-systems-with-medusajs-and-microservices-architecture-2mdh</link>
      <guid>https://dev.to/umoren/designing-scalable-and-high-performance-e-commerce-systems-with-medusajs-and-microservices-architecture-2mdh</guid>
      <description>&lt;p&gt;Have you built an e-commerce app or worked on an existing e-commerce system before? If you have, then you'll agree that e-commerce systems constantly evolve and become more complex, requiring businesses to have scalable and high-performing systems to remain competitive. That's where MedusaJS comes in. It is an open-source headless commerce engine that provides developers with a flexible framework for building modern e-commerce stores using any front-end technology.&lt;/p&gt;

&lt;p&gt;However, to truly maximize its potential, combining MedusaJS with microservices architecture can take your e-commerce system to the next level. Microservices architecture is an approach to software development that structures an application as a collection of loosely coupled services. Each service is responsible for a specific functionality, and they can all communicate with each other through APIs. Integrating MedusaJS with microservices allows you to build a modular and scalable e-commerce system that can handle high traffic and deliver exceptional performance.&lt;/p&gt;

&lt;p&gt;This guide explores the benefits of combining MedusaJS with microservices architecture and shows you how to design and implement a modular e-commerce solution. With MedusaJS and microservices architecture, you can create a modern, efficient, and flexible e-commerce platform that can keep up with the evolving demands of the market.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Benefits of Combining MedusaJS and Microservices Architecture
&lt;/h2&gt;

&lt;p&gt;When you combine MedusaJS with microservices architecture, you unlock numerous benefits that can lead to a more efficient and scalable e-commerce platform. Here are some key advantages of this approach:&lt;/p&gt;

&lt;h3&gt;
  
  
  Scalability
&lt;/h3&gt;

&lt;p&gt;E-commerce platforms must handle varying loads, including peak traffic during special promotions or seasonal events. By using microservices, you can independently scale individual components based on their specific needs. This enables you to optimize resource utilization while ensuring that your platform can handle increased demand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flexibility
&lt;/h3&gt;

&lt;p&gt;MedusaJS provides a flexible framework for building modern e-commerce stores, and combining it with microservices architecture further enhances this flexibility. Each service can be developed and deployed independently, allowing you to mix and match technologies that best fit your requirements. This modular approach makes it easier to update or replace components without affecting the entire system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enhanced Performance
&lt;/h3&gt;

&lt;p&gt;Microservices allow each component of your e-commerce system to operate independently, which can lead to improved performance. By decoupling services, you can allocate resources efficiently and minimize bottlenecks in your system. This results in a faster, more responsive e-commerce platform that can handle high traffic without sacrificing user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Designing a Modular E-commerce Solution with MedusaJS and Microservices&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To create a scalable and high-performing e-commerce platform, you need a well-thought-out design. Here are some key steps to designing a modular solution with MedusaJS and microservices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Define your services&lt;/strong&gt;: Identify the core functionalities of your e-commerce platform and break them down into individual services. For our example e-commerce platform, let's define the following services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Product Catalog Management&lt;/strong&gt;: Handles product-related operations, such as listing, searching, and updating product information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shopping Cart&lt;/strong&gt;: Manages customers' shopping carts and their operations, including adding, updating, and removing items.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payment Processing&lt;/strong&gt;: Processes payments and handles refund requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Order Management&lt;/strong&gt;: Manages orders from creation to fulfillment, including tracking order status and handling shipping.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Choose the right technology stack&lt;/strong&gt;: Pick the right technologies for each microservice based on your specific requirements. MedusaJS supports various technologies, allowing you to choose the most suitable stack for each service. For instance, consider the following stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend: Node.js and Express.js for building RESTful APIs&lt;/li&gt;
&lt;li&gt;Frontend: React for building a responsive user interface&lt;/li&gt;
&lt;li&gt;Database: PostgreSQL for storing e-commerce data&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extend MedusaJS with custom plugins&lt;/strong&gt;: MedusaJS provides a plugin architecture that allows you to extend its functionality to meet the needs of your specific use case. For example, you can create a custom plugin to handle order management:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// order-management-plugin.js&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;order-management&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Medusa&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Medusa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./models/order&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;Medusa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./routes/order&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;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Design the API contracts&lt;/strong&gt;: Establish the communication protocols between your microservices. Design the APIs for each service, ensuring that they are well-documented and easy to understand. MedusaJS allows you to define API routes for your services:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// routes/order.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Medusa&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&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="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Medusa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&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&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;router&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;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Implement proper monitoring and logging&lt;/strong&gt;: To maintain a high-performing e-commerce platform, implement robust monitoring and logging solutions for each service. This will help you identify performance bottlenecks and troubleshoot issues quickly. Consider using tools like Prometheus for monitoring and Winston for logging:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;winston&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createLogger&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="na"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;transports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;File&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application.log&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Medusa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;medusa&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;Medusa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;logger&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Implement API Gateway:&lt;/strong&gt; To integrate Medusajs with microservices for seamless interaction, it is important to implement an API Gateway to route requests to the appropriate microservices, handling authentication, load balancing, and request/response transformation. A basic API gateway looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// api-gateway.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;httpProxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http-proxy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;productServiceUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shoppingCartServiceUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:4000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orderServiceUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:5000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paymentServiceUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:6000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiProxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;httpProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createProxyServer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/products/*&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;apiProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;productServiceUrl&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&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/cart/*&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;apiProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;shoppingCartServiceUrl&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&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/orders/*&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;apiProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;orderServiceUrl&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&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/payments/*&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;apiProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;paymentServiceUrl&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`API Gateway listening on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Managing Data Consisitency and communication between services:&lt;/strong&gt; It is important that you implement strategies to manage data consistency and communication between the microservices because this is vital to the overall performance and reliablity of the e-commerce app. To manage data consistency and communication, consider the following approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data Synchronization Patterns: Implement data synchronization patterns such as eventual consistency, compensating transactions, or sagas to coordinate updates across multiple microservices.&lt;/li&gt;
&lt;li&gt;Message Broker: Use a message broker like Kafka or RabbitMQ to facilitate communication between microservices, allowing them to exchange messages and maintain data consistency asynchronously.&lt;/li&gt;
&lt;li&gt;Caching Strategies: Implement caching strategies to store frequently accessed data, reducing the need for inter-service communication and improving performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When building Microservices for e-commerce platforms with Medusajs, you’ll want to extend some of it’s functionalities. Medusa has a plugin system that allows developers to implement custom features or integrate third-party services into Medusa. Let’s explore this.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Implementing MedusaJS Plugins for Microservices&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Plugins can be used to add new endpoints, integrate with third-party services, or modify existing behavior to suit your application needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating custom plugins to extend MedusaJS functionality
&lt;/h3&gt;

&lt;p&gt;To create a custom plugin in MedusaJS, you can follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new file in your MedusaJS project directory and define a function that accepts a &lt;strong&gt;&lt;code&gt;Medusa&lt;/code&gt;&lt;/strong&gt; object as its argument. This function will be your plugin.&lt;/li&gt;
&lt;li&gt;Use the &lt;strong&gt;&lt;code&gt;Medusa.router()&lt;/code&gt;&lt;/strong&gt; function to create an instance of an Express router, and define your custom endpoint or middleware.&lt;/li&gt;
&lt;li&gt;Add the router to the Medusa app using the &lt;strong&gt;&lt;code&gt;Medusa.app.use()&lt;/code&gt;&lt;/strong&gt; method.&lt;/li&gt;
&lt;li&gt;Register your plugin with MedusaJS by adding the plugin file to the &lt;strong&gt;&lt;code&gt;plugins&lt;/code&gt;&lt;/strong&gt; array in your &lt;strong&gt;&lt;code&gt;medusa-config.js&lt;/code&gt;&lt;/strong&gt; file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s create a custom plugin for order management. &lt;/p&gt;

&lt;p&gt;Create a new file named &lt;strong&gt;&lt;code&gt;order-management-plugin.js&lt;/code&gt;&lt;/strong&gt; in your MedusaJS project directory and import the necessary dependencies, including the Medusa models/entities and any external libraries you may need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;medusa-core/models&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, define the plugin by exporting a function that receives a &lt;strong&gt;&lt;code&gt;Medusa&lt;/code&gt;&lt;/strong&gt; object as its argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;orderManagementPlugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Medusa&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Medusa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Define the custom endpoint for fetching orders&lt;/span&gt;
  &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/orders&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Fetch all orders from database&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="c1"&gt;// Return the orders to the client&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to fetch orders&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="c1"&gt;// Define a custom middleware for handling order fulfillment&lt;/span&gt;
  &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/fulfillment&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;order_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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;// Fetch the order from the database&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order_id&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="c1"&gt;// Call an external fulfillment API to fulfill the order&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://your-fulfillment-api.com&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="na"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;shipping_address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shipping_address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="c1"&gt;// Update the order status to "fulfilled"&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;fulfillment_status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fulfilled&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;fulfillment_response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="c1"&gt;// Call the next middleware in the chain&lt;/span&gt;
      &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Order fulfillment failed&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="c1"&gt;// Add the router to the Medusa app&lt;/span&gt;
  &lt;span class="nx"&gt;Medusa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;router&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;Here, we have two custom endpoints: one for fetching all orders from the database, and another for handling order fulfillment requests. The &lt;strong&gt;&lt;code&gt;/orders&lt;/code&gt;&lt;/strong&gt; endpoint uses the &lt;strong&gt;&lt;code&gt;Order&lt;/code&gt;&lt;/strong&gt; model from Medusa to fetch all orders from the database and return them to the client. The &lt;strong&gt;&lt;code&gt;/fulfillment&lt;/code&gt;&lt;/strong&gt; endpoint uses Axios to call an external fulfillment API and update the order's fulfillment status in the database.&lt;/p&gt;

&lt;p&gt;Next, register your plugin with MedusaJS by adding the following line to your &lt;strong&gt;&lt;code&gt;medusa-config.js&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./order-management-plugin.js&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;This line tells MedusaJS to load your custom order management plugin when starting the server.&lt;/p&gt;

&lt;p&gt;Learn more about &lt;a href="https://docs.medusajs.com/development/plugins/overview"&gt;Medusa Plugins&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;In conclusion, I hope this guide has been helpful in exploring the benefits of combining MedusaJS with microservices architecture and demonstrating how to design and implement a scalable and high-performing e-commerce solution.&lt;/p&gt;

&lt;p&gt;We covered several key concepts and techniques in this guide, including identifying and defining microservices, choosing the right technology stack, designing API contracts, implementing monitoring and logging, and creating custom plugins to extend MedusaJS functionality. By following these best practices and leveraging the power of MedusaJS and microservices architecture, you can build a modern, efficient, and flexible e-commerce platform that can keep up with the evolving demands of the market.&lt;/p&gt;

&lt;p&gt;Looking ahead, headless commerce and microservices architecture are set to continue growing in popularity as businesses seek to stay competitive in the rapidly evolving e-commerce landscape.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.medusajs.com/"&gt;MedusaJS Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://martinfowler.com/articles/microservices.html"&gt;Microservices Architecture&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>medusajs</category>
      <category>microservices</category>
      <category>node</category>
    </item>
  </channel>
</rss>
