<?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: Victor Webster</title>
    <description>The latest articles on DEV Community by Victor Webster (@chukwurahvictor).</description>
    <link>https://dev.to/chukwurahvictor</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F308113%2F7fa6f952-46fe-452e-a871-25dbf8a3fb56.JPG</url>
      <title>DEV Community: Victor Webster</title>
      <link>https://dev.to/chukwurahvictor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chukwurahvictor"/>
    <language>en</language>
    <item>
      <title>Designing a Secure Two-Tier Cloud Architecture on Google Cloud to Survive Traffic Spikes</title>
      <dc:creator>Victor Webster</dc:creator>
      <pubDate>Sat, 01 Aug 2026 02:27:02 +0000</pubDate>
      <link>https://dev.to/chukwurahvictor/designing-a-secure-two-tier-cloud-architecture-on-google-cloud-to-survive-traffic-spikes-1998</link>
      <guid>https://dev.to/chukwurahvictor/designing-a-secure-two-tier-cloud-architecture-on-google-cloud-to-survive-traffic-spikes-1998</guid>
      <description>&lt;p&gt;When you're building an application, it's tempting to deploy everything onto a single virtual machine. It's simple, inexpensive, and gets you to production quickly. For many startups, that's exactly how things begin.&lt;/p&gt;

&lt;p&gt;Imagine FreshCart, a fast-growing online grocery platform. During most of the week, a single server comfortably handles browsing, product searches, and checkout requests. Then the marketing team launches a "Weekend Mega Sale" campaign.&lt;/p&gt;

&lt;p&gt;Within minutes, thousands of users visit the site.&lt;/p&gt;

&lt;p&gt;Pages begin loading slowly. Checkout requests queue up. CPU utilization reaches nearly 100%, memory becomes exhausted, and eventually the server stops responding. Every customer is trying to access the same machine, and because that machine hosts the application and is directly exposed to the internet, it becomes both the performance bottleneck and the biggest security risk.&lt;/p&gt;

&lt;p&gt;This scenario is common because a single exposed VM has several limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every request is handled by one server.&lt;/li&gt;
&lt;li&gt;If that server fails, the application becomes unavailable.&lt;/li&gt;
&lt;li&gt;The backend is directly reachable from the internet, increasing the attack surface.&lt;/li&gt;
&lt;li&gt;Scaling often means replacing the VM with a larger one instead of distributing traffic across multiple servers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To address these problems, I designed and built a secure two-tier architecture on &lt;strong&gt;Google Cloud Platform (GCP)&lt;/strong&gt;. The architecture separates public traffic from the application layer, removes direct internet access to backend servers, and introduces redundancy so the application can continue serving requests even if one backend instance becomes unavailable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;The architecture consists of two logical tiers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Presentation Layer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Global HTTP Load Balancer&lt;/li&gt;
&lt;li&gt;Static website hosted in Cloud Storage&lt;/li&gt;
&lt;li&gt;HTTPS managed by Google-managed certificates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Application Layer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two backend Compute Engine instances&lt;/li&gt;
&lt;li&gt;Private subnet&lt;/li&gt;
&lt;li&gt;No public IP addresses&lt;/li&gt;
&lt;li&gt;Cloud NAT for outbound internet access only&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The backend servers are never directly exposed to users. Every request must first pass through Google's Load Balancer, which distributes traffic across healthy backend instances.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Insert your final architecture diagram here.)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture Components
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Custom VPC
&lt;/h3&gt;

&lt;p&gt;The first step was creating a dedicated Virtual Private Cloud (VPC).&lt;/p&gt;

&lt;p&gt;Rather than using Google's default network, I created a custom VPC to have complete control over IP ranges, firewall rules, and network segmentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute networks create &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VPC_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--subnet-mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;custom &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bgp-routing-mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;regional
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using a custom VPC keeps the infrastructure isolated and easier to manage as the application grows.&lt;/p&gt;




&lt;h3&gt;
  
  
  Public and Private Subnets
&lt;/h3&gt;

&lt;p&gt;Next, I created separate subnets.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public subnet&lt;/li&gt;
&lt;li&gt;Private subnet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The public subnet is intended for internet-facing resources, while backend application servers reside entirely inside the private subnet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute networks subnets create &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SUBNET_PUBLIC&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

gcloud compute networks subnets create &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$SUBNET_PRIVATE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The private subnet also enables &lt;strong&gt;Private Google Access&lt;/strong&gt;, allowing instances without public IPs to communicate securely with Google services.&lt;/p&gt;




&lt;h3&gt;
  
  
  Restrictive Firewall Rules
&lt;/h3&gt;

&lt;p&gt;Instead of allowing unrestricted access, firewall rules follow the principle of least privilege.&lt;/p&gt;

&lt;p&gt;SSH access is permitted &lt;strong&gt;only&lt;/strong&gt; through Google's Identity-Aware Proxy (IAP).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute firewall-rules create allow-ssh-iap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This eliminates the need to expose port 22 to the internet.&lt;/p&gt;

&lt;p&gt;Similarly, backend servers only accept HTTP traffic from Google's Load Balancer health check ranges.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute firewall-rules create allow-lb-health-check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because users cannot directly reach the backend VMs, the attack surface is significantly reduced.&lt;/p&gt;




&lt;h3&gt;
  
  
  Cloud NAT
&lt;/h3&gt;

&lt;p&gt;Backend servers require internet connectivity for activities such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installing packages&lt;/li&gt;
&lt;li&gt;Downloading updates&lt;/li&gt;
&lt;li&gt;Pulling dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, exposing them with public IP addresses would undermine the security model.&lt;/p&gt;

&lt;p&gt;Cloud NAT solves this problem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute routers create &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ROUTER_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

gcloud compute routers nats create &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NAT_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cloud NAT provides outbound internet connectivity while preventing unsolicited inbound connections.&lt;/p&gt;

&lt;p&gt;This means the backend servers can install software updates without ever becoming publicly accessible.&lt;/p&gt;




&lt;h3&gt;
  
  
  Backend Compute Engine Instances
&lt;/h3&gt;

&lt;p&gt;To eliminate a single point of failure, I deployed two backend servers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zone A&lt;/li&gt;
&lt;li&gt;Zone B&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each VM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;uses the lightweight &lt;strong&gt;e2-micro&lt;/strong&gt; machine type&lt;/li&gt;
&lt;li&gt;has &lt;strong&gt;no external IP address&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;resides inside the private subnet
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute instances create freshcart-backend-a
gcloud compute instances create freshcart-backend-b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploying backend instances across multiple zones increases availability.&lt;/p&gt;

&lt;p&gt;If one zone experiences an outage or maintenance event, traffic can continue flowing to the healthy instance in the remaining zone.&lt;/p&gt;




&lt;h3&gt;
  
  
  Global HTTP Load Balancer
&lt;/h3&gt;

&lt;p&gt;Instead of allowing users to connect directly to backend servers, every request first reaches Google's Global HTTP Load Balancer.&lt;/p&gt;

&lt;p&gt;The load balancer performs continuous health checks against the backend instances.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute health-checks create http freshcart-health-check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Healthy instances receive traffic.&lt;/p&gt;

&lt;p&gt;Unhealthy instances are automatically removed from rotation.&lt;/p&gt;

&lt;p&gt;The backend service combines both instance groups into a single logical service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute backend-services create freshcart-backend-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, a URL map, HTTP proxy, and forwarding rule expose one public endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute forwarding-rules create freshcart-http-rule
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This architecture dramatically improves resilience during traffic spikes.&lt;/p&gt;

&lt;p&gt;Instead of overwhelming a single server, incoming requests are distributed between multiple healthy backend instances.&lt;/p&gt;




&lt;h3&gt;
  
  
  Static Website Hosting
&lt;/h3&gt;

&lt;p&gt;FreshCart's landing page, images, JavaScript, and CSS are excellent candidates for static hosting.&lt;/p&gt;

&lt;p&gt;Rather than serving these files from the backend VMs, they are stored in Google Cloud Storage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud storage buckets create &lt;span class="s2"&gt;"gs://&lt;/span&gt;&lt;span class="nv"&gt;$BUCKET_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Backend Bucket connects Cloud Storage to Google's Load Balancer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute backend-buckets create freshcart-static-backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A managed SSL certificate provides HTTPS without manual certificate management.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute ssl-certificates create freshcart-managed-cert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Separating static content from the backend significantly reduces unnecessary requests hitting the application servers.&lt;/p&gt;

&lt;p&gt;For example, if 20,000 visitors all request the homepage logo, CSS, and JavaScript, those assets are served directly from Cloud Storage rather than consuming backend CPU cycles.&lt;/p&gt;




&lt;h3&gt;
  
  
  Lifecycle Management
&lt;/h3&gt;

&lt;p&gt;Static assets accumulate over time.&lt;/p&gt;

&lt;p&gt;Rather than storing old files indefinitely in expensive Standard Storage, I configured a lifecycle policy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud storage buckets update &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--lifecycle-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/lifecycle.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Objects older than &lt;strong&gt;30 days&lt;/strong&gt; automatically transition to &lt;strong&gt;Nearline Storage&lt;/strong&gt;, reducing storage costs.&lt;/p&gt;

&lt;p&gt;Objects older than &lt;strong&gt;90 days&lt;/strong&gt; are automatically deleted.&lt;/p&gt;

&lt;p&gt;This ensures long-term storage costs remain predictable without requiring manual cleanup.&lt;/p&gt;




&lt;h2&gt;
  
  
  How This Architecture Handles Traffic Spikes
&lt;/h2&gt;

&lt;p&gt;Returning to FreshCart's flash sale scenario, consider what happens when thousands of customers visit the website simultaneously.&lt;/p&gt;

&lt;p&gt;Instead of every request hitting a single VM, users first connect to Google's Global HTTP Load Balancer.&lt;/p&gt;

&lt;p&gt;The load balancer distributes incoming traffic across both backend instances. If one backend becomes overloaded or unhealthy, health checks detect the issue and automatically stop routing requests to it. Customers continue interacting with the remaining healthy backend while the unhealthy instance is repaired or replaced.&lt;/p&gt;

&lt;p&gt;At the same time, static resources such as HTML, images, CSS, and JavaScript are served directly from Cloud Storage over HTTPS. These requests never reach the backend servers, significantly reducing CPU utilization and improving response times.&lt;/p&gt;

&lt;p&gt;The backend servers remain private throughout this process. They can still install security updates and software packages using Cloud NAT, but because they have no public IP addresses, attackers cannot connect to them directly.&lt;/p&gt;

&lt;p&gt;The result is an architecture that is considerably more resilient, secure, and scalable than a single exposed virtual machine.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Design Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Why two backend instances instead of one?
&lt;/h3&gt;

&lt;p&gt;Running a single VM would have been cheaper and simpler, but it would also remain a single point of failure.&lt;/p&gt;

&lt;p&gt;Deploying backend instances across two availability zones increases fault tolerance. If one instance or zone becomes unavailable, the load balancer automatically routes traffic to the remaining healthy backend.&lt;/p&gt;

&lt;p&gt;The trade-off is slightly higher infrastructure cost, but the improvement in availability justifies the additional expense.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Why remove public IP addresses?
&lt;/h3&gt;

&lt;p&gt;Many beginner deployments expose backend servers directly to the internet.&lt;/p&gt;

&lt;p&gt;I deliberately avoided this.&lt;/p&gt;

&lt;p&gt;Instead, every backend VM resides inside a private subnet and communicates externally only through Cloud NAT.&lt;/p&gt;

&lt;p&gt;This dramatically reduces the attack surface while still allowing package updates and outbound connections.&lt;/p&gt;

&lt;p&gt;The trade-off is that administrators must connect using Identity-Aware Proxy instead of traditional SSH over the public internet.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Why separate static content from the backend?
&lt;/h3&gt;

&lt;p&gt;Initially, it might seem easier to let the backend server host everything.&lt;/p&gt;

&lt;p&gt;However, every image, stylesheet, and JavaScript file consumes server resources.&lt;/p&gt;

&lt;p&gt;Hosting static assets in Cloud Storage allows the backend to focus exclusively on business logic such as authentication, inventory, and checkout.&lt;/p&gt;

&lt;p&gt;This improves scalability and reduces infrastructure costs.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Would Build Next
&lt;/h2&gt;

&lt;p&gt;If FreshCart continued growing, this architecture would evolve further.&lt;/p&gt;

&lt;p&gt;The first improvement would be replacing unmanaged instance groups with &lt;strong&gt;Managed Instance Groups&lt;/strong&gt; to enable automatic scaling during periods of high demand.&lt;/p&gt;

&lt;p&gt;I would also place &lt;strong&gt;Cloud CDN&lt;/strong&gt; in front of the Cloud Storage bucket so static assets are cached at Google's global edge locations, reducing latency for customers worldwide.&lt;/p&gt;

&lt;p&gt;For observability, I would integrate &lt;strong&gt;Cloud Monitoring&lt;/strong&gt;, &lt;strong&gt;Cloud Logging&lt;/strong&gt;, and alerting policies to monitor CPU usage, request latency, backend health, and application errors in real time.&lt;/p&gt;

&lt;p&gt;Finally, I would migrate the application database to &lt;strong&gt;Cloud SQL&lt;/strong&gt; configured for high availability, ensuring the data layer matches the resilience of the application tier.&lt;/p&gt;




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

&lt;p&gt;Designing cloud infrastructure isn't simply about deploying servers—it's about making deliberate architectural decisions that balance security, scalability, availability, and cost.&lt;/p&gt;

&lt;p&gt;By introducing a custom VPC, private backend instances, Cloud NAT, a Global HTTP Load Balancer, Cloud Storage for static assets, and automated lifecycle policies, FreshCart is now significantly better equipped to withstand sudden traffic spikes than it would be with a single exposed virtual machine.&lt;/p&gt;

&lt;p&gt;This architecture provides a solid production-ready foundation while remaining simple enough to understand, extend, and discuss during technical interviews or portfolio reviews.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>cloud</category>
      <category>infrastructure</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Build a simple react application to implement the contextApi (useContext)</title>
      <dc:creator>Victor Webster</dc:creator>
      <pubDate>Tue, 10 Jan 2023 22:08:34 +0000</pubDate>
      <link>https://dev.to/chukwurahvictor/build-a-simple-react-application-to-implement-the-contextapi-usecontext-2dae</link>
      <guid>https://dev.to/chukwurahvictor/build-a-simple-react-application-to-implement-the-contextapi-usecontext-2dae</guid>
      <description>&lt;p&gt;Let's build a simple react application to implement the contextApi.&lt;/p&gt;

&lt;p&gt;Create a new react app using the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following dependencies will be required for the application:&lt;br&gt;
&lt;strong&gt;react-router-dom&lt;/strong&gt;: To help with routing between pages in our application&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;react-error-boundary&lt;/strong&gt;: To help with implementing error boundary&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tailwindcss&lt;/strong&gt;: A low utility css framework to help with styling our application&lt;/p&gt;

&lt;p&gt;To install these dependencies, use the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install react-router-dom react-error-boundary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's talk briefly about the project structure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzx9zoj8ugmc608gsbvc2.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzx9zoj8ugmc608gsbvc2.PNG" alt="project-structure" width="449" height="539"&gt;&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Components&lt;/strong&gt;: This folder will hold all our reusable components like Navbar, Sidebar, etc.&lt;br&gt;
&lt;strong&gt;Context&lt;/strong&gt;: This folder will house our userContext configuration file.&lt;br&gt;
&lt;strong&gt;Hooks&lt;/strong&gt;: This will hold our custom hooks file.&lt;br&gt;
&lt;strong&gt;Pages&lt;/strong&gt;: The different pages in our application will be here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build the Navbar component&lt;/strong&gt;&lt;br&gt;
Inside the components folder, create a Navbar.jsx file and put this code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState, useContext } from 'react';
import { UserContext } from "../context/UserContext";

const Navbar = () =&amp;gt; {
  const [navbar, setNavbar] = useState(false);
  const { isAuth, userInfo, logout } = useContext(UserContext);

  const onLogout = (e) =&amp;gt; {
    e.preventDefault();
    logout();
  };

   return (
     &amp;lt;nav className="w-full bg-blue-800 shadow"&amp;gt;
       &amp;lt;div className="justify-between px-4 mx-auto lg:max-w-7xl md:items-center md:flex md:px-8"&amp;gt;
         &amp;lt;div&amp;gt;
           &amp;lt;div className="flex items-center justify-between py-3 md:py-5 md:block"&amp;gt;
             &amp;lt;a href="/"&amp;gt;
               &amp;lt;h2 className="text-2xl font-bold text-white"&amp;gt;AltSchool&amp;lt;/h2&amp;gt;
             &amp;lt;/a&amp;gt;
             &amp;lt;div className="md:hidden"&amp;gt;
               &amp;lt;button
                 className="p-2 text-gray-700 rounded-md outline-none focus:border-gray-400 focus:border"
                 onClick={() =&amp;gt; setNavbar(!navbar)}
               &amp;gt;
                 {navbar ? (
                   &amp;lt;svg
                     xmlns="http://www.w3.org/2000/svg"
                     className="w-6 h-6 text-white"
                     viewBox="0 0 20 20"
                     fill="currentColor"
                   &amp;gt;
                     &amp;lt;path
                       fillRule="evenodd"
                       d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
                       clipRule="evenodd"
                     /&amp;gt;
                   &amp;lt;/svg&amp;gt;
                 ) : (
                   &amp;lt;svg
                     xmlns="http://www.w3.org/2000/svg"
                     className="w-6 h-6 text-white"
                     fill="none"
                     viewBox="0 0 24 24"
                     stroke="currentColor"
                     strokeWidth={2}
                   &amp;gt;
                     &amp;lt;path
                       strokeLinecap="round"
                       strokeLinejoin="round"
                       d="M4 6h16M4 12h16M4 18h16"
                     /&amp;gt;
                   &amp;lt;/svg&amp;gt;
                 )}
               &amp;lt;/button&amp;gt;
             &amp;lt;/div&amp;gt;
           &amp;lt;/div&amp;gt;
         &amp;lt;/div&amp;gt;
         &amp;lt;div&amp;gt;
           &amp;lt;div
             className={`flex-1 justify-self-center pb-3 mt-8 md:block md:pb-0 md:mt-0 ${
               navbar ? "block" : "hidden"
             }`}
           &amp;gt;
             &amp;lt;ul className="items-center justify-center space-y-8 md:flex md:space-x-6 md:space-y-0"&amp;gt;
               &amp;lt;li className="text-white hover:text-indigo-200"&amp;gt;
                 &amp;lt;a href="/"&amp;gt;Home&amp;lt;/a&amp;gt;
               &amp;lt;/li&amp;gt;
               &amp;lt;li className="text-white hover:text-indigo-200"&amp;gt;
                 &amp;lt;a href="/about"&amp;gt;About&amp;lt;/a&amp;gt;
               &amp;lt;/li&amp;gt;
               &amp;lt;li className="text-white hover:text-indigo-200"&amp;gt;
                 &amp;lt;a href="/contact"&amp;gt;Contact&amp;lt;/a&amp;gt;
               &amp;lt;/li&amp;gt;
             &amp;lt;/ul&amp;gt;

             &amp;lt;div className="mt-3 space-y-2 lg:hidden md:hidden"&amp;gt;
               {!isAuth ? (
                 &amp;lt;a
                   href="/login"
                   className="inline-block w-full px-4 py-2 text-center text-white bg-gray-600 rounded-md shadow hover:bg-gray-800"
                 &amp;gt;
                   Sign in
                 &amp;lt;/a&amp;gt;
               ) : (
                 &amp;lt;&amp;gt;
                   &amp;lt;p className="inline-block w-full px-4 py-2 text-center text-white"&amp;gt;
                     Welcome, {userInfo?.username}
                   &amp;lt;/p&amp;gt;
                   &amp;lt;button
                     onClick={onLogout}
                     className="inline-block w-full px-4 py-2 text-center text-white bg-gray-600 rounded-md shadow hover:bg-gray-800"
                   &amp;gt;
                     Logout
                   &amp;lt;/button&amp;gt;
                 &amp;lt;/&amp;gt;
               )}
             &amp;lt;/div&amp;gt;
           &amp;lt;/div&amp;gt;
         &amp;lt;/div&amp;gt;
         &amp;lt;div className="hidden space-x-2 md:flex"&amp;gt;
           {!isAuth ? (
             &amp;lt;a
               href="/login"
               className="px-4 py-2 text-white bg-gray-600 rounded-md shadow hover:bg-gray-800"
             &amp;gt;
               Sign in
             &amp;lt;/a&amp;gt;
           ) : (
             &amp;lt;&amp;gt;
               &amp;lt;div class="overflow-hidden relative w-10 h-10 bg-gray-100 rounded-full dark:bg-gray-600"&amp;gt;
                 &amp;lt;svg
                   class="absolute -left-1 w-12 h-12 text-gray-400"
                   fill="currentColor"
                   viewBox="0 0 20 20"
                   xmlns="http://www.w3.org/2000/svg"
                 &amp;gt;
                   &amp;lt;path
                     fill-rule="evenodd"
                     d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
                     clip-rule="evenodd"
                   &amp;gt;&amp;lt;/path&amp;gt;
                 &amp;lt;/svg&amp;gt;
               &amp;lt;/div&amp;gt;
               &amp;lt;div className="px-4 py-2 text-white"&amp;gt;
                 &amp;lt;p&amp;gt;Welcome, {userInfo?.username}&amp;lt;/p&amp;gt;
               &amp;lt;/div&amp;gt;
               &amp;lt;button
                 onClick={onLogout}
                 className="px-4 py-2 text-white bg-gray-600 rounded-md shadow hover:bg-gray-800"
               &amp;gt;
                 Logout
               &amp;lt;/button&amp;gt;
             &amp;lt;/&amp;gt;
           )}
         &amp;lt;/div&amp;gt;
       &amp;lt;/div&amp;gt;
     &amp;lt;/nav&amp;gt;
   );
   }

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

&lt;/div&gt;



&lt;p&gt;Inside of the App.js file, import the navbar component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Navbar from "./components/Navbar.jsx";

const App = () =&amp;gt; {
  return (
    &amp;lt;div className="App"&amp;gt;
      &amp;lt;Navbar /&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Set up userContext&lt;/strong&gt;&lt;br&gt;
Inside the context folder, create a userContext.js file and put this code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { createContext, useState, useEffect } from "react";

const initialState = {
  userInfo: null,
  isAuth: false,
};

export const UserContext = createContext(initialState);

export const UserContextProvider = ({ children }) =&amp;gt; {
  const [userInfo, setUserInfo] = useState(null);
  const [isAuth, setIsAuth] = useState(false);

  useEffect(() =&amp;gt; {
    const storedUser = localStorage.getItem("user");
    if (storedUser) {
      setUserInfo(JSON.parse(storedUser));
      setIsAuth(true);
    }
  }, []);

  const login = (username, password) =&amp;gt; {
    setIsAuth(true);
    setUserInfo({ username, password });
    localStorage.setItem("user", JSON.stringify({username, password}));
    localStorage.setItem("auth", "true");
  };

  const logout = () =&amp;gt; {
    fetch("/logout").then((res) =&amp;gt; {
      setIsAuth(false);
      setUserInfo(null);
      localStorage.removeItem("user");
      localStorage.removeItem("auth");
    });
  };

   const value = {
      userInfo,
      setUserInfo,
      isAuth,
      setIsAuth,
      login,
      logout,
   };

   return (
      &amp;lt;UserContext.Provider value={value}&amp;gt; {children} &amp;lt;/UserContext.Provider&amp;gt;
   );
};

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

&lt;/div&gt;



&lt;p&gt;We start by setting our default states for the user which is null for userInfo and false for isAuth. We can then use the createContext by react to create the userContext using the default states. Next, we create the contextProvider which takes a props of children and also create functions to handle login and logout in our application. When a user logs in, we update the user state and also store the updated user state in the localStorage to persist the details i.e it does not return to default on refresh.&lt;/p&gt;

&lt;p&gt;We should have something like this on our screen when we run our application.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Set up Routing&lt;/strong&gt;&lt;br&gt;
Now, let's set up our routing. First, we build all the pages and components needed like our dashboard, 404, Login, About and Contact pages and import them into out App.js file then we set up error boundary using the react-error-boundary package we installed earlier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { ErrorBoundary } from "react-error-boundary";

import Home from "./pages/Home";
import About from "./pages/About";
import Contact from "./pages/Contact";
import NotFound from "./pages/404"
import Dashboard from "./pages/Dashboard";
import Navbar from "./components/Navbar.jsx";
import Login from './pages/Login';

const ErrorFallback = ({ error }) =&amp;gt; {
  return (
    &amp;lt;div role="alert"&amp;gt;
      &amp;lt;p&amp;gt;Something went wrong:&amp;lt;/p&amp;gt;
      &amp;lt;pre style={{ color: "red" }}&amp;gt;{error.message}&amp;lt;/pre&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};
const App = () =&amp;gt; {
  return (
    &amp;lt;div className="App"&amp;gt;
      &amp;lt;ErrorBoundary FallbackComponent={ErrorFallback}&amp;gt;
        &amp;lt;Navbar /&amp;gt;
        &amp;lt;Router&amp;gt;
          &amp;lt;Routes&amp;gt;
            &amp;lt;Route path="/" element={&amp;lt;Home /&amp;gt;} /&amp;gt;
            &amp;lt;Route path="/login" element={&amp;lt;Login /&amp;gt;} /&amp;gt;
            &amp;lt;Route path="/dashboard" element={&amp;lt;Dashboard /&amp;gt;}/&amp;gt;
            &amp;lt;Route path="/about" element={&amp;lt;About /&amp;gt;} /&amp;gt;
            &amp;lt;Route path="/contact" element={&amp;lt;Contact /&amp;gt;} /&amp;gt;
            &amp;lt;Route path="*" element={&amp;lt;NotFound /&amp;gt;} /&amp;gt;
          &amp;lt;/Routes&amp;gt;
        &amp;lt;/Router&amp;gt;
      &amp;lt;/ErrorBoundary&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Set up Custom hook&lt;/strong&gt;&lt;br&gt;
We will now set a custom hook to get the user from the userContext and can be used anywhere in our code. Inside the hooks folder, create a getUser.js file and add this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useContext } from 'react';
import { UserContext } from "../context/UserContext";

const useGetUser = () =&amp;gt; {
  const { userInfo, isAuth } = useContext(UserContext);
  return {userInfo, isAuth};
};

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

&lt;/div&gt;



&lt;p&gt;The useGetUser hook can now be used anywhere in our code to get the current logged in user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set up protected routing&lt;/strong&gt;&lt;br&gt;
The protected routing is used to protect our routes from being accessed by unauthorized users. So, a user has to be logged in before they can access a route that is protected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Navigate } from "react-router-dom";

export const ProtectedRoute = ({ children }) =&amp;gt; {
   const isAuth = localStorage.getItem("auth");
   if (!isAuth) {
     // user is not authenticated
     return &amp;lt;Navigate to="/login" /&amp;gt;;
   }
   return children;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import the protectedRoute into the App.js file and wrap the elements of the routes you want to protect like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            &amp;lt;Route
              path="/dashboard"
              element={
                &amp;lt;ProtectedRoute&amp;gt;
                  &amp;lt;Dashboard /&amp;gt;
                &amp;lt;/ProtectedRoute&amp;gt;
              }
            /&amp;gt;
            &amp;lt;Route
              path="/about"
              element={
                &amp;lt;ProtectedRoute&amp;gt;
                  &amp;lt;About /&amp;gt;
                &amp;lt;/ProtectedRoute&amp;gt;
              }
            /&amp;gt;
            &amp;lt;Route
              path="/contact"
              element={
                &amp;lt;ProtectedRoute&amp;gt;
                  &amp;lt;Contact /&amp;gt;
                &amp;lt;/ProtectedRoute&amp;gt;
              }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project should look something like this at the end:&lt;/p&gt;

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

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

&lt;p&gt;The link to the complete project has been provided below as well as the project link and other important documentations for packages and dependencies used in this application. In case you encounter errors or have any questions, you can go through the links or ask me in the comment section.&lt;/p&gt;

&lt;p&gt;Thank you for reading!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Github Repo&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/ChukwurahVictor/altschool_exam" rel="noopener noreferrer"&gt;https://github.com/ChukwurahVictor/altschool_exam&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project live link&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://zippy-kheer-15c249.netlify.app/" rel="noopener noreferrer"&gt;https://zippy-kheer-15c249.netlify.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tailwind Docs&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://tailwindcss.com/docs/guides/create-react-app" rel="noopener noreferrer"&gt;https://tailwindcss.com/docs/guides/create-react-app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>career</category>
    </item>
  </channel>
</rss>
