<?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>Forem: Michelle </title>
    <description>The latest articles on Forem by Michelle  (@michellewanjiru).</description>
    <link>https://forem.com/michellewanjiru</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%2F3718292%2F587d92b2-0144-4ec4-8e63-bef9d7f4d0ef.jpg</url>
      <title>Forem: Michelle </title>
      <link>https://forem.com/michellewanjiru</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/michellewanjiru"/>
    <language>en</language>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 7 — Docker Compose: One Command to Rule Them All</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Sun, 12 Apr 2026 14:35:05 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-7-docker-compose-one-command-to-rule-them-all-1ll</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-7-docker-compose-one-command-to-rule-them-all-1ll</guid>
      <description>&lt;p&gt;&lt;strong&gt;Yesterday, I had two containers: one for my backend, one for my frontend.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But running them meant two terminals, two &lt;code&gt;docker run&lt;/code&gt; commands, and remembering which port mapped to what.&lt;/p&gt;

&lt;p&gt;Today, I fixed that forever.&lt;/p&gt;

&lt;p&gt;I learned Docker Compose a tool that lets me define my entire application stack in one file and start everything with a SINGLE command.&lt;/p&gt;




&lt;h2&gt;
  
  
  First: What Problem Does Docker Compose Solve?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Without Docker Compose (Days 5 &amp;amp; 6)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Terminal 1&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;backend
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; backend-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 3001:5000 backend-app

&lt;span class="c"&gt;# Terminal 2 (new terminal)&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;frontend
npm run build
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; frontend-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 frontend-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The pain:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple terminals&lt;/li&gt;
&lt;li&gt;Many commands to remember&lt;/li&gt;
&lt;li&gt;Containers don't automatically know about each other&lt;/li&gt;
&lt;li&gt;Hard to share setup with others&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  With Docker Compose (Today)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# One terminal. One command.&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;That's it.&lt;/strong&gt; Everything runs.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Docker Compose?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;In one sentence:&lt;/strong&gt; Docker Compose is a tool that lets you define and run multiple Docker containers together using a single YAML file.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Analogy
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Docker Alone&lt;/th&gt;
&lt;th&gt;Docker Compose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Individual containers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Shipping containers&lt;/td&gt;
&lt;td&gt;Shipping containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Managing them&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Moving each by hand&lt;/td&gt;
&lt;td&gt;A crane operator with a manifest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Documentation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Remember what goes where&lt;/td&gt;
&lt;td&gt;Everything in one file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Docker Compose is the crane operator + manifest that organizes all your containers at once.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Creating docker-compose.yml
&lt;/h2&gt;

&lt;p&gt;First, I went to my project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Desktop/Production-Ready-Microservices-Platform
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I created the Compose file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;docker-compose.yml
nano docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Writing the Compose File (Line by Line)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Line 1: Version
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;3.8'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this means:&lt;/strong&gt; Which version of Docker Compose syntax to use (3.8 is current).&lt;/p&gt;

&lt;h3&gt;
  
  
  Line 2: Services Section
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this means:&lt;/strong&gt; Everything below defines containers (called "services").&lt;/p&gt;

&lt;h3&gt;
  
  
  Lines 3-6: Backend Service
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./backend&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3001:5000"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Line&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;backend:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name of the service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;build: ./backend&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build image using Dockerfile in &lt;code&gt;./backend&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ports:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Define port mapping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;- "3001:5000"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map host 3001 → container 5000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Lines 7-10: Frontend Service
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./frontend&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:80"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same pattern, but for the frontend (port 8080 → 80).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Complete File
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;3.8'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./backend&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3001:5000"&lt;/span&gt;

  &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./frontend&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:80"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;That's it!&lt;/strong&gt; 12 lines to define my entire application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: What Docker Compose Does Behind the Scenes
&lt;/h2&gt;

&lt;p&gt;When I ran &lt;code&gt;docker-compose up --build&lt;/code&gt;, Compose did ALL of this automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Created a shared network&lt;/span&gt;
docker network create microservices-platform_default

&lt;span class="c"&gt;# 2. Built backend image&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; microservices-platform-backend ./backend

&lt;span class="c"&gt;# 3. Built frontend image&lt;/span&gt;
docker build &lt;span class="nt"&gt;-t&lt;/span&gt; microservices-platform-frontend ./frontend

&lt;span class="c"&gt;# 4. Started backend container&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; microservices-platform-backend-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; microservices-platform_default &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 3001:5000 &lt;span class="se"&gt;\&lt;/span&gt;
  microservices-platform-backend

&lt;span class="c"&gt;# 5. Started frontend container&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; microservices-platform-frontend-1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt; microservices-platform_default &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="se"&gt;\&lt;/span&gt;
  microservices-platform-frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;All of that from one command.&lt;/strong&gt; That's the magic of Docker Compose.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Running Everything
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What I saw:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[+] Building 2.5s (15/15) FINISHED
[+] Running 3/3
 ✔ backend   Built
 ✔ frontend  Built
 ✔ Network microservices-platform_default  Created
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the containers started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;frontend-1  | /docker-entrypoint.sh: Configuration complete;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ready &lt;span class="k"&gt;for &lt;/span&gt;start up
&lt;span class="go"&gt;backend-1   | Server is running on http://localhost:5000
backend-1   | Try these endpoints:
backend-1   |   - http://localhost:5000/
backend-1   |   - http://localhost:5000/health
backend-1   |   - http://localhost:5000/users
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Both containers running simultaneously in ONE terminal!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Testing Everything
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Testing the Backend
&lt;/h3&gt;

&lt;p&gt;In a new terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3001/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&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="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"healthy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"backend-api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2026-04-12T13:10:49.057Z"&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;
  
  
  Testing the Frontend
&lt;/h3&gt;

&lt;p&gt;In my browser: &lt;code&gt;http://localhost:8080&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My React app appeared!&lt;/strong&gt; Same as Day 3, but now running inside a container managed by Compose.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Stopping Everything
&lt;/h2&gt;

&lt;p&gt;To stop all containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# In the Compose terminal, press Ctrl+C&lt;/span&gt;
Ctrl+C

&lt;span class="c"&gt;# Then clean up&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker-compose down
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;One command stops EVERYTHING.&lt;/strong&gt; No more hunting for container IDs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Docker vs Docker Compose: The Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Docker Alone&lt;/th&gt;
&lt;th&gt;Docker Compose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Run one container&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker run ...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Overkill&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Run multiple containers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple commands&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker-compose up&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Connect containers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual network setup&lt;/td&gt;
&lt;td&gt;Automatic network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Start order&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual (wait, then start)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;depends_on&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Environment variables&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;-e&lt;/code&gt; flag each time&lt;/td&gt;
&lt;td&gt;In file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Port mapping&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;-p&lt;/code&gt; flag each time&lt;/td&gt;
&lt;td&gt;In file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Share setup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Send commands to friend&lt;/td&gt;
&lt;td&gt;Send one file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rebuild after changes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual rebuild&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;--build&lt;/code&gt; flag&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;View logs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker logs ID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docker-compose logs&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Why Your Project NEEDS Docker Compose
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Your Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User → Frontend (React) → Backend (Node.js) → (Soon: PostgreSQL)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Each component has different needs:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need&lt;/th&gt;
&lt;th&gt;Frontend&lt;/th&gt;
&lt;th&gt;Backend&lt;/th&gt;
&lt;th&gt;Database (coming)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Base image&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;nginx:alpine&lt;/td&gt;
&lt;td&gt;node:18-alpine&lt;/td&gt;
&lt;td&gt;postgres:15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Port&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;5000&lt;/td&gt;
&lt;td&gt;5432&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Persistent volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Start order&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Last&lt;/td&gt;
&lt;td&gt;Middle&lt;/td&gt;
&lt;td&gt;First&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scale for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;User traffic&lt;/td&gt;
&lt;td&gt;API calls&lt;/td&gt;
&lt;td&gt;Data growth&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Without Compose (Manual Nightmare)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start database&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; postgres &lt;span class="nt"&gt;-v&lt;/span&gt; postgres_data:/var/lib/postgresql/data postgres:15

&lt;span class="c"&gt;# Wait for database&lt;/span&gt;
&lt;span class="nb"&gt;sleep &lt;/span&gt;5

&lt;span class="c"&gt;# Start backend&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; backend &lt;span class="nt"&gt;--link&lt;/span&gt; postgres &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;DB_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres backend-app

&lt;span class="c"&gt;# Start frontend&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; frontend &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 frontend-app

&lt;span class="c"&gt;# Create network&lt;/span&gt;
docker network create myapp
docker network connect myapp postgres backend frontend

&lt;span class="c"&gt;# To stop: 4 separate commands&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;15+ commands!&lt;/strong&gt; One mistake and nothing works.&lt;/p&gt;

&lt;h3&gt;
  
  
  With Compose (One File)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&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;3.8'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:15&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres_data:/var/lib/postgresql/data&lt;/span&gt;

  &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./backend&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_HOST=postgres&lt;/span&gt;

  &lt;span class="na"&gt;frontend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./frontend&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ONE command&lt;/span&gt;
docker-compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3 commands total.&lt;/strong&gt; Perfect every time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Issues I Ran Into
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue 1: "no configuration file provided"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;no configuration file provided: not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; I was in the wrong directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Desktop/Production-Ready-Microservices-Platform
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; docker-compose.yml  &lt;span class="c"&gt;# Verify file exists&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 2: Port already allocated
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bind for 0.0.0.0:8080 failed: port is already allocated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; I still had my old frontend container running from Day 6.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker ps  &lt;span class="c"&gt;# Find running containers&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker stop &amp;lt;container_id&amp;gt;  &lt;span class="c"&gt;# Stop the old one&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker-compose up  &lt;span class="c"&gt;# Now it works&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 3: The "version is obsolete" warning
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;the attribute `version` is obsolete, it will be ignored
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; I can remove the &lt;code&gt;version: '3.8'&lt;/code&gt; line — newer Docker versions don't need it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Docker Compose Commands Cheat Sheet
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose up&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start all services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose up -d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start in background (detached)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose up --build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rebuild images then start&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose down&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop and remove containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose down -v&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Also remove volumes (databases!)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show status of services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose logs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View logs from all services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose logs backend&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;View logs from backend only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rebuild images without starting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker-compose restart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restart all services&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Docker Compose = Multiple containers, one command&lt;/strong&gt; — No more terminal juggling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything in one file&lt;/strong&gt; — Share your setup with anyone&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic networking&lt;/strong&gt; — Containers can find each other by name&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Declarative configuration&lt;/strong&gt; — Say WHAT you want, not HOW&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local = Production&lt;/strong&gt; — Same Compose file works everywhere&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker Compose Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/compose/compose-file/" rel="noopener noreferrer"&gt;Compose File Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/blog/what-is-docker-compose-vs-kubernetes/" rel="noopener noreferrer"&gt;Docker Compose vs Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>devops</category>
      <category>docker</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 6 — Dockerizing My React Frontend</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Sat, 11 Apr 2026 14:55:23 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-6-dockerizing-my-react-frontend-4na7</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-6-dockerizing-my-react-frontend-4na7</guid>
      <description>&lt;p&gt;&lt;strong&gt;Yesterday, I packaged my backend into a Docker container. Today, I did the same for my React frontend.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But there's a twist, frontend containers are DIFFERENT. No Node.js running. No &lt;code&gt;npm start&lt;/code&gt;. Just pure, static files served by nginx.&lt;/p&gt;

&lt;p&gt;And I learned why nginx is the king of web servers.&lt;/p&gt;




&lt;h2&gt;
  
  
  First: How Frontend Docker is Different
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Backend Container&lt;/th&gt;
&lt;th&gt;Frontend Container&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Runs Node.js server&lt;/td&gt;
&lt;td&gt;Runs nginx (web server)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Needs &lt;code&gt;npm start&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Just serves static files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code changes often&lt;/td&gt;
&lt;td&gt;Build once, serve forever&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Port 5000&lt;/td&gt;
&lt;td&gt;Port 80 (standard web port)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;~135MB image&lt;/td&gt;
&lt;td&gt;~23MB image&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;ul&gt;
&lt;li&gt;Backend = Restaurant kitchen (active cooking, handling requests)&lt;/li&gt;
&lt;li&gt;Frontend = Dining room + menu (just shows what's ready)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Building React for Production
&lt;/h2&gt;

&lt;p&gt;Right now, I run React with &lt;code&gt;npm start&lt;/code&gt; (development mode). For Docker, I need a &lt;strong&gt;production build&lt;/strong&gt;, optimized, minified, and ready to serve.&lt;/p&gt;

&lt;h3&gt;
  
  
  Navigate to frontend folder
&lt;/h3&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; ~/Desktop/Production-Ready-Microservices-Platform/frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build the production version
&lt;/h3&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;&lt;strong&gt;What this command does:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Takes all my React code&lt;/li&gt;
&lt;li&gt;Optimizes it (removes comments, shortens variable names)&lt;/li&gt;
&lt;li&gt;Minifies files (makes them smaller)&lt;/li&gt;
&lt;li&gt;Creates a &lt;code&gt;build&lt;/code&gt; folder with everything needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What I saw:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Creating an optimized production build...
Compiled successfully.

File sizes after gzip:
  40.21 kB  build/static/js/main.xxxxx.js
  1.78 kB   build/static/css/main.xxxxx.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify the build folder
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I could see the &lt;code&gt;build&lt;/code&gt; folder (blue or highlighted in 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;ls &lt;/span&gt;build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;index.html  static  asset-manifest.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What's inside:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;index.html&lt;/code&gt; — The main HTML file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;static/&lt;/code&gt; — JavaScript and CSS files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;asset-manifest.json&lt;/code&gt; — Map of all assets&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 2: Creating the Dockerfile
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why nginx?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;nginx (pronounced "engine-x")&lt;/strong&gt; is the most popular web server in the world. It's:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Why It Matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fast&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can handle 10,000+ connections simultaneously&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lightweight&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Alpine version is only 23MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reliable&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Used by Netflix, Airbnb, Uber, Dropbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Simple&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Just point it to your files and it works&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Create the Dockerfile
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Complete Dockerfile
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; build /usr/share/nginx/html&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;That's it!&lt;/strong&gt; Three lines. Frontend containers are much simpler than backend.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding Each Line
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Line&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FROM nginx:alpine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Start with nginx on Alpine Linux&lt;/td&gt;
&lt;td&gt;Tiny base image (23MB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COPY build /usr/share/nginx/html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Put React files where nginx looks&lt;/td&gt;
&lt;td&gt;nginx serves from this folder by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EXPOSE 80&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Document the port&lt;/td&gt;
&lt;td&gt;Standard web port&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What is &lt;code&gt;/usr/share/nginx/html&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;This is nginx's &lt;strong&gt;default document root&lt;/strong&gt; — the folder where it looks for files to serve. When you put &lt;code&gt;index.html&lt;/code&gt; there, nginx automatically serves it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Save and verify
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Save: Ctrl+O, Enter, Ctrl+X&lt;/span&gt;

&lt;span class="c"&gt;# Verify the file&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; nginx:alpine&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; build /usr/share/nginx/html&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Building the Frontend Image
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; frontend-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking down the command:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo docker build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-t frontend-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tag it as "frontend-app"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use Dockerfile in current directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What I saw:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[+] Building 0.5s (5/5) FINISHED
&lt;/span&gt;&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;1/2] FROM nginx:alpine
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;2/2] COPY build /usr/share/nginx/html
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;exporting to image
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; naming to docker.io/library/frontend-app:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verify the image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;REPOSITORY     TAG       IMAGE ID       CREATED         SIZE
frontend-app   latest    xxxxxxx        1 minute ago    23MB
backend-app    latest    e0e0904815be   1 day ago       135MB
nginx          alpine    xxxxxxx        2 weeks ago     23MB
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Notice the size difference:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend image: 135MB (Node.js + dependencies)&lt;/li&gt;
&lt;li&gt;Frontend image: 23MB (just static files + nginx)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 4: Running the Frontend Container
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 frontend-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Understanding the port mapping:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-p 8080:80&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map port 8080 (my computer) → port 80 (container)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;frontend-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Which image to use&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why port 80?&lt;/strong&gt; Port 80 is the standard web port. Browsers use it by default when you visit &lt;code&gt;http://example.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why map to 8080?&lt;/strong&gt; Port 80 is often already in use. 8080 is a common alternative.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I saw:&lt;/strong&gt; Nothing in the terminal. That's normal! nginx runs silently.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Testing the Container
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Open browser
&lt;/h3&gt;

&lt;p&gt;I went to: &lt;code&gt;http://localhost:8080&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My React app appeared!&lt;/strong&gt; The same one I built on Day 3.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check running containers
&lt;/h3&gt;

&lt;p&gt;In a &lt;strong&gt;new terminal&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS
&lt;/span&gt;&lt;span class="gp"&gt;b8194975c784   frontend-app   "/docker-entrypoint.…"   3 minutes ago   Up 3 minutes   0.0.0.0:8080-&amp;gt;&lt;/span&gt;80/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this shows:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Container is running&lt;/li&gt;
&lt;li&gt;Port 8080 on my computer → port 80 inside container&lt;/li&gt;
&lt;li&gt;Status: &lt;code&gt;Up&lt;/code&gt; (healthy!)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Running Both Containers
&lt;/h2&gt;

&lt;p&gt;Now I have TWO containers running:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Container&lt;/th&gt;
&lt;th&gt;Image&lt;/th&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;frontend-app&lt;/td&gt;
&lt;td&gt;nginx:alpine&lt;/td&gt;
&lt;td&gt;8080&lt;/td&gt;
&lt;td&gt;Serves React app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;backend-app&lt;/td&gt;
&lt;td&gt;node:18-alpine&lt;/td&gt;
&lt;td&gt;3001&lt;/td&gt;
&lt;td&gt;API endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;To see both:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;CONTAINER ID   IMAGE           COMMAND                  PORTS
&lt;/span&gt;&lt;span class="gp"&gt;b8194975c784   frontend-app    "/docker-entrypoint.…"   0.0.0.0:8080-&amp;gt;&lt;/span&gt;80/tcp
&lt;span class="gp"&gt;a1b2c3d4e5f6   backend-app     "docker-entrypoint.s…"   0.0.0.0:3001-&amp;gt;&lt;/span&gt;5000/tcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Stopping the Container
&lt;/h2&gt;

&lt;p&gt;To stop the frontend container, I went to its terminal and pressed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ctrl + C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The container stopped gracefully.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why nginx for Frontend?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Alternative&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;serve&lt;/code&gt; (npm package)&lt;/td&gt;
&lt;td&gt;Simple&lt;/td&gt;
&lt;td&gt;Needs Node.js installed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;http-server&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Easy&lt;/td&gt;
&lt;td&gt;Not production-ready&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;nginx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Fast, reliable, tiny&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Slight learning curve&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Production Build vs Development
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;npm start&lt;/code&gt; (dev)&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;npm run build&lt;/code&gt; (prod)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File size&lt;/td&gt;
&lt;td&gt;Large (unminified)&lt;/td&gt;
&lt;td&gt;Small (minified)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source maps&lt;/td&gt;
&lt;td&gt;Yes (debugging)&lt;/td&gt;
&lt;td&gt;No (smaller)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hot reload&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Slow&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use case&lt;/td&gt;
&lt;td&gt;Development&lt;/td&gt;
&lt;td&gt;Production/Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Build Process Explained
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React Source Code (JSX, Components)
        ↓
    npm run build
        ↓
Optimized Files (minified, bundled)
        ↓
   build/ folder
        ↓
    COPY to nginx
        ↓
     Served to users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Mistakes I Made (And Fixed)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mistake 1: Trying to use &lt;code&gt;npm start&lt;/code&gt; in Docker
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What I thought:&lt;/strong&gt; I could just do &lt;code&gt;CMD ["npm", "start"]&lt;/code&gt; like backend&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it's wrong:&lt;/strong&gt; React's dev server isn't meant for production containers&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Use nginx to serve static files&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 2: Wrong COPY destination
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What I tried:&lt;/strong&gt; &lt;code&gt;COPY build /var/www/html&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it's wrong:&lt;/strong&gt; nginx default folder is &lt;code&gt;/usr/share/nginx/html&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Use the correct nginx document root&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 3: Forgetting to run &lt;code&gt;npm run build&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; Docker couldn't find the &lt;code&gt;build&lt;/code&gt; folder&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt; &lt;code&gt;COPY failed: stat build: file does not exist&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Always run &lt;code&gt;npm run build&lt;/code&gt; before building the Docker image&lt;/p&gt;




&lt;h2&gt;
  
  
  Docker Commands Cheat Sheet (Updated)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create production build of React app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker build -t name .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build image from Dockerfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker images&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -p HOST:CONTAINER image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run container from image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List running containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List ALL containers (including stopped)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker stop container_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop running container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rm container_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove stopped container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rmi image_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove image&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Frontend containers are simpler&lt;/strong&gt; — Just static files + web server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;nginx is the industry standard&lt;/strong&gt; — 40% of all websites use it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production builds are essential&lt;/strong&gt; — &lt;code&gt;npm run build&lt;/code&gt; creates optimized files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alpine images are tiny&lt;/strong&gt; — My frontend image is only 23MB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port mapping is still important&lt;/strong&gt; — Container port 80 → host port 8080&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/_/nginx" rel="noopener noreferrer"&gt;nginx Official Image&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://create-react-app.dev/docs/production-build/" rel="noopener noreferrer"&gt;React Production Build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.nginx.com/blog/nginx-vs-apache-vs-node-js/" rel="noopener noreferrer"&gt;nginx vs Apache vs Node.js for serving static files&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's Connect!
&lt;/h2&gt;

&lt;p&gt;Have you containerized a frontend app before? What web server do you prefer? nginx, Apache, or something else?&lt;/p&gt;

&lt;p&gt;Drop a comment or connect on LinkedIn. Let's learn together! &lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is Day 6 of my 30-Day Cloud &amp;amp; DevOps Challenge. Follow along as I build a complete microservices platform from scratch!&lt;/em&gt;&lt;/p&gt;



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



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

&lt;/div&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>frontend</category>
      <category>react</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 5 — Dockerizing My Backend (First Steps into Containers)</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Sat, 11 Apr 2026 14:28:01 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-5-dockerizing-my-backend-first-steps-into-containers-356e</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-5-dockerizing-my-backend-first-steps-into-containers-356e</guid>
      <description>&lt;p&gt;&lt;strong&gt;Yesterday, I had a working PostgreSQL database with real data. But there was one problem...&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My backend still ran with &lt;code&gt;npm run dev&lt;/code&gt;. It worked on MY laptop, but would it work anywhere else?&lt;/p&gt;

&lt;p&gt;Today, I solved that forever.&lt;/p&gt;

&lt;p&gt;I packaged my Node.js backend into a Docker container — a self-contained unit that runs IDENTICALLY on any computer. And I learned why containers are revolutionizing software deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  First: What Even IS Docker? (Explained Simply)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Problem Docker Solves
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before Docker (The "It works on my machine" nightmare):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Computer&lt;/th&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Your laptop&lt;/td&gt;
&lt;td&gt;Node.js v18, PostgreSQL local&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Your coworker's laptop&lt;/td&gt;
&lt;td&gt;Node.js v14, different OS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud server&lt;/td&gt;
&lt;td&gt;Ubuntu, missing dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production&lt;/td&gt;
&lt;td&gt;Completely different environment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; "But it worked on MY machine!" &lt;/p&gt;

&lt;h3&gt;
  
  
  The Docker Solution
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Think of Docker like a shipping container:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Analogy&lt;/th&gt;
&lt;th&gt;Docker&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shipping container&lt;/td&gt;
&lt;td&gt;Docker container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ship, train, truck&lt;/td&gt;
&lt;td&gt;Any computer running Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Your stuff inside&lt;/td&gt;
&lt;td&gt;Your app + Node.js + all dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The magic:&lt;/strong&gt; Once it's in a container, it runs IDENTICALLY everywhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Checking Docker Installation
&lt;/h2&gt;

&lt;p&gt;First, I needed to verify Docker was installed on my Ubuntu system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Docker version 29.2.1, build a5c7197
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker was installed! But I hit a permission issue:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;permission denied while trying to connect to the docker API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fixing Docker Permissions
&lt;/h3&gt;

&lt;p&gt;The issue: My user wasn't in the &lt;code&gt;docker&lt;/code&gt; group.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Add my user to docker group&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker mkangeth

&lt;span class="c"&gt;# Log out and back in (or restart)&lt;/span&gt;
&lt;span class="c"&gt;# Then verify&lt;/span&gt;
docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Alternative (what I used):&lt;/strong&gt; Added &lt;code&gt;sudo&lt;/code&gt; before each docker command:&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;sudo &lt;/span&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Empty list = Docker is working! &lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Creating the Dockerfile
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;Dockerfile&lt;/code&gt; is like a &lt;strong&gt;recipe&lt;/strong&gt; that tells Docker how to build your container.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a Dockerfile?
&lt;/h3&gt;

&lt;p&gt;Think of it like baking a cake:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Baking Analogy&lt;/th&gt;
&lt;th&gt;Dockerfile Instruction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Start with base ingredients&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FROM node:18-alpine&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set up your workspace&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WORKDIR /app&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add your ingredients&lt;/td&gt;
&lt;td&gt;&lt;code&gt;COPY package*.json ./&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mix everything&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RUN npm install&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Present the final cake&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CMD ["npm", "start"]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Creating the File
&lt;/h3&gt;

&lt;p&gt;First, navigate to my backend folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Desktop/Production-Ready-Microservices-Platform/backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the Dockerfile:&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;touch &lt;/span&gt;Dockerfile
nano Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Complete Dockerfile (Line by Line)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use Node.js 18 on Alpine Linux (tiny Linux, only 5MB!)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18-alpine&lt;/span&gt;

&lt;span class="c"&gt;# Create and set working directory inside the container&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="c"&gt;# Copy package files first (for Docker caching)&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;

&lt;span class="c"&gt;# Install all dependencies&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Copy all source code&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;

&lt;span class="c"&gt;# Tell Docker the container listens on port 5000&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 5000&lt;/span&gt;

&lt;span class="c"&gt;# Start the application&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "start"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why Each Line Matters
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Instruction&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;Why it's important&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FROM node:18-alpine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Base image with Node.js&lt;/td&gt;
&lt;td&gt;Alpine is tiny (5MB vs 100MB+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WORKDIR /app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sets working directory&lt;/td&gt;
&lt;td&gt;All commands run from here&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COPY package*.json ./&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copies dependency list&lt;/td&gt;
&lt;td&gt;Docker caches this layer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RUN npm install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Installs dependencies&lt;/td&gt;
&lt;td&gt;Happens inside the container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COPY . .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copies your code&lt;/td&gt;
&lt;td&gt;Last layer (changes most often)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EXPOSE 5000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Documents the port&lt;/td&gt;
&lt;td&gt;Doesn't publish, just informs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CMD ["npm", "start"]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Runs when container starts&lt;/td&gt;
&lt;td&gt;Production command, not dev&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 3: Building the Docker Image
&lt;/h2&gt;

&lt;p&gt;Now the magic happens — building my image:&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;sudo &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; backend-app &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Breaking down the command:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo docker build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build an image from a Dockerfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-t backend-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tag (name) the image "backend-app"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use Dockerfile in current directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;What I saw:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[+] Building 45.2s (10/10) FINISHED
&lt;/span&gt;&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;1/5] FROM node:18-alpine
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;2/5] WORKDIR /app
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;3/5] COPY package&lt;span class="k"&gt;*&lt;/span&gt;.json ./
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;4/5] RUN npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;5/5] COPY &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;exporting to image
&lt;span class="gp"&gt; =&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; naming to docker.io/library/backend-app:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Verifying the Image
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;docker images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;REPOSITORY     TAG       IMAGE ID       CREATED         SIZE
backend-app    latest    e0e0904815be   2 minutes ago   135MB
node           18-alpine f0286cc18189   2 weeks ago     120MB
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My image is 135MB — much smaller than a full Ubuntu image would be (thanks, Alpine!)&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Running the Container
&lt;/h2&gt;

&lt;p&gt;Time to run my backend inside a container:&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;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 3001:5000 backend-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Understanding port mapping:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-p 3001:5000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Map port 3001 (my computer) → port 5000 (container)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;backend-app&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Which image to use&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why port mapping?&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inside container: app runs on port 5000&lt;/li&gt;
&lt;li&gt;On my computer: I can access it via port 3001&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;backend@1.0.0 start
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;node server.js
&lt;span class="go"&gt;
 Server is running on http://localhost:5000
 Try these endpoints:
   - http://localhost:5000/
   - http://localhost:5000/health
   - http://localhost:5000/users
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The terminal stays "stuck" here&lt;/strong&gt; — that's GOOD! My server is running!&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Testing the Container
&lt;/h2&gt;

&lt;p&gt;In a &lt;strong&gt;new terminal&lt;/strong&gt;, I tested my running container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3001/health
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&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="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"healthy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"backend-api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2026-04-11T14:03:42.057Z"&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;p&gt;Then tested the users endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3001/users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&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="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"alice@example.com"&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="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"bob@example.com"&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="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Charlie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"charlie@example.com"&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;p&gt;&lt;strong&gt;SUCCESS!&lt;/strong&gt; My Dockerized backend was working perfectly!&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Stopping the Container
&lt;/h2&gt;

&lt;p&gt;To stop the container, I went back to the first terminal and pressed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ctrl + C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server shut down gracefully.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Docker Concepts I Mastered
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What I learned&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Image&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A blueprint/template (like a class in programming)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Container&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A running instance of an image (like an object)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dockerfile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The recipe that defines how to build an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Port mapping&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Connecting container ports to host ports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Layer caching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Docker reuses unchanged layers for faster builds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Docker Workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dockerfile → docker build → Image → docker run → Container
   (recipe)      (build)     (blueprint)   (run)    (running app)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why Alpine Linux?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Base Image&lt;/th&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node:latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~1GB&lt;/td&gt;
&lt;td&gt;Full OS, many tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node:18-slim&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~200MB&lt;/td&gt;
&lt;td&gt;Minimal Debian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node:18-alpine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~120MB&lt;/td&gt;
&lt;td&gt;Tiny, security-focused&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;I chose Alpine:&lt;/strong&gt; Smaller = faster downloads, less storage, smaller attack surface.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mistakes I Made (And Fixed)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mistake 1: Docker Permission Denied
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;permission denied while trying to connect to the docker API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Added &lt;code&gt;sudo&lt;/code&gt; before docker commands OR added user to docker group:&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;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;
&lt;span class="c"&gt;# Then log out and back in&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mistake 2: Wrong Port Mapping
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; My server ran on port 5000, but I mapped to port 8000&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Wrong&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 3001:8000 backend-app

&lt;span class="c"&gt;# Correct&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 3001:5000 backend-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; Always check what port your app actually uses!&lt;/p&gt;




&lt;h2&gt;
  
  
  Docker Commands Cheat Sheet
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker --version&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check Docker version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker images&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all images on your computer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List RUNNING containers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker ps -a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List ALL containers (including stopped)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker build -t name .&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build an image from Dockerfile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker run -p HOST:CONTAINER image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a container from an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker stop container_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop a running container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rm container_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove a stopped container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker rmi image_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove an image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docker logs container_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;See container logs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Containers are NOT virtual machines&lt;/strong&gt; — They share the host OS kernel, making them lightweight and fast&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dockerfiles are recipes&lt;/strong&gt; — They define exactly how to build your container&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Images are blueprints&lt;/strong&gt; — You build once, run anywhere&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Containers are running instances&lt;/strong&gt; — You can run many containers from one image&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port mapping is essential&lt;/strong&gt; — Containers have their own network; you must expose ports&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alpine Linux is your friend&lt;/strong&gt; — Tiny images = faster everything&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/" rel="noopener noreferrer"&gt;Docker Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/reference/builder/" rel="noopener noreferrer"&gt;Dockerfile Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md" rel="noopener noreferrer"&gt;Node.js Docker Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://alpinelinux.org/" rel="noopener noreferrer"&gt;Alpine Linux (tiny Docker images)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's Connect!
&lt;/h2&gt;

&lt;p&gt;Have you used Docker before? What was your first container? Any tips for someone just starting?&lt;/p&gt;

&lt;p&gt;Drop a comment or connect on LinkedIn. Let's learn together! &lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is Day 5 of my 30-Day Cloud &amp;amp; DevOps Challenge. Follow along as I build a complete microservices platform from scratch!&lt;/em&gt;&lt;/p&gt;



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



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

&lt;/div&gt;

</description>
      <category>devchallenge</category>
      <category>devops</category>
      <category>docker</category>
      <category>node</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 4 — PostgreSQL Deep Dive (From Zero to Database Hero)</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Wed, 08 Apr 2026 09:13:28 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-4-postgresql-deep-dive-from-zero-to-database-hero-23e9</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-4-postgresql-deep-dive-from-zero-to-database-hero-23e9</guid>
      <description>&lt;p&gt;Yesterday, my React frontend finally talked to my Node.js backend. But there was one problem...&lt;/p&gt;

&lt;p&gt;The users were &lt;strong&gt;FAKE&lt;/strong&gt;. Hardcoded in &lt;code&gt;server.js&lt;/code&gt;. Every time the server restarted, any new users would disappear forever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Today, I fixed that permanently.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I installed PostgreSQL, created a database, built a users table, and stored real data that survives server restarts. And I learned WAY more about databases than I expected.&lt;/p&gt;




&lt;h2&gt;
  
  
  First: What Even IS a Database?
&lt;/h2&gt;

&lt;p&gt;Before writing a single command, I needed to understand what a database actually IS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Think of it like this:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Real World&lt;/th&gt;
&lt;th&gt;Database&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A filing cabinet&lt;/td&gt;
&lt;td&gt;The database itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A drawer in the cabinet&lt;/td&gt;
&lt;td&gt;A table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A folder in the drawer&lt;/td&gt;
&lt;td&gt;A row (record)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Information on the folder&lt;/td&gt;
&lt;td&gt;Columns (fields)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  For my users:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;One drawer called &lt;code&gt;users&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Each folder has: an ID number, a name, an email address, a creation date&lt;/li&gt;
&lt;li&gt;I can open any folder, read it, change it, or throw it away&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The magic:&lt;/strong&gt; Even if I turn off my computer, the filing cabinet (database) still has all my folders.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Installing PostgreSQL
&lt;/h2&gt;

&lt;p&gt;I'm on Ubuntu Linux, so installation was straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Update my package list (like refreshing the app store)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update

&lt;span class="c"&gt;# Install PostgreSQL&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;postgresql postgresql-contrib &lt;span class="nt"&gt;-y&lt;/span&gt;

&lt;span class="c"&gt;# Verify it's running&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status postgresql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What I learned about each command:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it actually does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"Super user do" — gives admin permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apt update&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Refreshes the list of available software&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apt install postgresql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Downloads and installs the database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-y&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Automatically answers "yes" to confirmation prompts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The moment I knew it worked:&lt;/strong&gt; Seeing &lt;code&gt;Active: active (running)&lt;/code&gt; in green text.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Understanding PostgreSQL Users
&lt;/h2&gt;

&lt;p&gt;When PostgreSQL installs, it automatically creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A system user called &lt;code&gt;postgres&lt;/code&gt; (the admin/manager)&lt;/li&gt;
&lt;li&gt;A default database also called &lt;code&gt;postgres&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Analogy:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Analogy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;A bank vault&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;postgres&lt;/code&gt; user&lt;/td&gt;
&lt;td&gt;The bank manager (has all the keys)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;My job&lt;/td&gt;
&lt;td&gt;Ask the manager to create a vault for MY project&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  To become the manager:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; postgres psql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;My prompt changed from:&lt;/strong&gt; &lt;code&gt;mkangeth@LAP-018:~$&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;To:&lt;/strong&gt; &lt;code&gt;postgres=#&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; That &lt;code&gt;postgres=#&lt;/code&gt; means I'm INSIDE PostgreSQL now. Different commands work here.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Step 3: My First PostgreSQL Commands
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Seeing what databases already exist:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This showed me three databases: &lt;code&gt;postgres&lt;/code&gt;, &lt;code&gt;template0&lt;/code&gt;, &lt;code&gt;template1&lt;/code&gt;. None were mine.&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating MY database:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;microservices_platform&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;CREATE DATABASE&lt;/code&gt; — it worked!&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating a user for my app:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;app_user&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="s1"&gt;'mysecretpassword'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;CREATE ROLE&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Giving my user permission:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;microservices_platform&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;app_user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;GRANT&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Connecting to MY database:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="n"&gt;microservices_platform&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;You are now connected to database "microservices_platform"&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Notice:&lt;/strong&gt; My prompt changed to &lt;code&gt;microservices_platform=#&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Step 4: Creating My First Table
&lt;/h2&gt;

&lt;p&gt;Now for the exciting part — creating a table to store users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;SERIAL&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="k"&gt;CURRENT_TIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Let me explain EVERY piece:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Part&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CREATE TABLE users&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Make a new table called "users"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id SERIAL PRIMARY KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Auto-number each user (1,2,3...) and use it as the unique identifier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name VARCHAR(100) NOT NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Name column, max 100 characters, can't be empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;email VARCHAR(100) UNIQUE NOT NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Email column, max 100 chars, no duplicates, can't be empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Automatically sets the current time when a user is added&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;CREATE TABLE&lt;/code&gt; — my table exists!&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Inserting Data
&lt;/h2&gt;

&lt;p&gt;Time to add some users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; 
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Alice'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'alice@example.com'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Bob'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'bob@example.com'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Charlie'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'charlie@example.com'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;INSERT 0 3&lt;/code&gt; — three users added successfully.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Reading My Data
&lt;/h2&gt;

&lt;p&gt;The moment of truth — seeing what's actually in my database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="n"&gt;name&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt;        &lt;span class="n"&gt;email&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="n"&gt;created_at&lt;/span&gt;
&lt;span class="c1"&gt;----+---------+---------------------+----------------------------&lt;/span&gt;
 &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Alice&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;alice&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;   &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;360654&lt;/span&gt;
 &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Bob&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bob&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;     &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;360654&lt;/span&gt;
 &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Charlie&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;charlie&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;360654&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;I literally cheered.&lt;/strong&gt; My data was stored permanently in a real database!&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Exiting PostgreSQL
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back to my normal terminal prompt.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Learned About Databases (Deep Dive)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why PostgreSQL Specifically?
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Why It Matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ACID compliance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transactions are atomic — either fully complete or fully fail (no partial updates)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MVCC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multiple users can read/write simultaneously without locking each other&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Advanced data types&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JSON, arrays, hstore, geometric data — not just strings and numbers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Standards compliant&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Learning PostgreSQL teaches you SQL that works on Oracle, DB2, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Data Types I Used
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;What it stores&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SERIAL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Auto-incrementing integer&lt;/td&gt;
&lt;td&gt;1, 2, 3, 4...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;VARCHAR(100)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text up to 100 characters&lt;/td&gt;
&lt;td&gt;"Alice"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TIMESTAMP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Date and time&lt;/td&gt;
&lt;td&gt;"2026-04-08 09:57:56"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Constraints I Used
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PRIMARY KEY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uniquely identifies each row (no duplicates, never null)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NOT NULL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;This field must have a value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;UNIQUE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No two rows can have the same value in this column&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DEFAULT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If no value is provided, use this automatic value&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Mistakes I Made (And Fixed)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mistake 1: Typing PostgreSQL commands in the normal terminal
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; I typed &lt;code&gt;\c&lt;/code&gt; at &lt;code&gt;mkangeth@LAP-018:~$&lt;/code&gt; and got &lt;code&gt;command not found&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; &lt;code&gt;\c&lt;/code&gt; is a PostgreSQL command, not a Linux command&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Enter PostgreSQL first with &lt;code&gt;sudo -u postgres psql&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 2: Forgetting the semicolon
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; I typed &lt;code&gt;CREATE DATABASE microservices_platform&lt;/code&gt; and nothing happened&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; PostgreSQL waits for &lt;code&gt;;&lt;/code&gt; to know the command is complete&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Always end SQL commands with &lt;code&gt;;&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 3: Incomplete CREATE DATABASE
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; I typed &lt;code&gt;CREATE DATABASE microservices&lt;/code&gt; and got stuck at a &lt;code&gt;postgres-#&lt;/code&gt; prompt&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt; I didn't finish the command&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Press &lt;code&gt;Ctrl+C&lt;/code&gt; to cancel, then retype the full command&lt;/p&gt;




&lt;h2&gt;
  
  
  PostgreSQL vs Other Databases (What I Learned)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Database&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Complex apps, data integrity&lt;/td&gt;
&lt;td&gt;Full SQL support, ACID compliant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MySQL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Simple web apps, WordPress&lt;/td&gt;
&lt;td&gt;Fast reads, easier to learn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SQLite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mobile apps, local storage&lt;/td&gt;
&lt;td&gt;No server needed, single file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MongoDB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Flexible schema, JSON data&lt;/td&gt;
&lt;td&gt;No fixed structure, horizontal scaling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Firebase&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Real-time apps, prototyping&lt;/td&gt;
&lt;td&gt;Built-in auth, real-time updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Supabase&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL + convenience&lt;/td&gt;
&lt;td&gt;Open source, real-time, auto-API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Why I chose PostgreSQL for this project:&lt;/strong&gt; It's the industry standard for serious applications. Apple, Netflix, and Uber all use it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Databases are permanent storage&lt;/strong&gt; — unlike server memory, data survives restarts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tables have structure&lt;/strong&gt; — columns define what data looks like (types, constraints)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL is powerful&lt;/strong&gt; — ACID compliance, advanced data types, concurrency handling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQL is the language&lt;/strong&gt; — &lt;code&gt;CREATE&lt;/code&gt;, &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The prompt tells you where you are&lt;/strong&gt; — &lt;code&gt;$&lt;/code&gt; = terminal, &lt;code&gt;=#&lt;/code&gt; = PostgreSQL&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Quick Reference: PostgreSQL Commands I Learned
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sudo -u postgres psql&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enter PostgreSQL as admin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\l&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all databases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\c database_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Connect to a specific database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List all tables in current database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\d table_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show table structure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CREATE DATABASE name;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a new database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CREATE USER name WITH PASSWORD 'pass';&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a new user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GRANT ALL PRIVILEGES ON DATABASE db TO user;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Give user permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CREATE TABLE ...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create a new table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INSERT INTO ... VALUES ...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Add data to a table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SELECT * FROM ...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read data from a table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\q&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Exit PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/docs/" rel="noopener noreferrer"&gt;PostgreSQL Official Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3schools.com/sql/" rel="noopener noreferrer"&gt;SQL Tutorial for Beginners&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/about/advantages/" rel="noopener noreferrer"&gt;PostgreSQL vs MySQL Comparison&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;







&lt;h2&gt;
  
  
  Let's Connect!
&lt;/h2&gt;

&lt;p&gt;Have you worked with databases before? SQL or NoSQL? What's your preference and why?&lt;/p&gt;

&lt;p&gt;Drop a comment or connect on LinkedIn. Let's learn together! &lt;/p&gt;




</description>
      <category>beginners</category>
      <category>database</category>
      <category>devchallenge</category>
      <category>postgres</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 3 — Connecting Frontend to Backend (The CORS Journey)</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Fri, 03 Apr 2026 10:42:26 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-3-connecting-frontend-to-backend-the-cors-journey-2be9</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-3-connecting-frontend-to-backend-the-cors-journey-2be9</guid>
      <description>&lt;p&gt;After yesterday's backend success, today was about making my React frontend actually &lt;strong&gt;TALK&lt;/strong&gt; to my Node.js API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spoiler alert:&lt;/strong&gt; It wasn't as simple as I thought. But that's where the real learning happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Set Out to Do Today
&lt;/h2&gt;

&lt;p&gt;Build a React frontend that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Connects to my backend API&lt;/li&gt;
&lt;li&gt;✅ Fetches health status automatically&lt;/li&gt;
&lt;li&gt;✅ Displays users from the database&lt;/li&gt;
&lt;li&gt;✅ Shows everything in a nice UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Simple, right?&lt;/strong&gt; Well... let me share the journey.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Creating the React App
&lt;/h2&gt;

&lt;p&gt;I started by creating a React app in my project folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/Desktop/Production-Ready-Microservices-Platform
npx create-react-app frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This created a complete React application with all the boilerplate code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt; &lt;code&gt;create-react-app&lt;/code&gt; is a magical command that sets up an entire React development environment in minutes. It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Development server with hot reloading&lt;/li&gt;
&lt;li&gt;Build tools (webpack, Babel)&lt;/li&gt;
&lt;li&gt;Testing setup&lt;/li&gt;
&lt;li&gt;Production build optimization&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 2: Cleaning Up Unnecessary Files
&lt;/h2&gt;

&lt;p&gt;React creates many example files. I removed what I didn't need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;frontend/src
&lt;span class="nb"&gt;rm &lt;/span&gt;App.test.js setupTests.js reportWebVitals.js logo.svg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt; &lt;code&gt;reportWebVitals.js&lt;/code&gt; tracks performance metrics (load time, responsiveness). Not needed for learning, but useful for production apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Oops moment:&lt;/strong&gt; After deleting files, React crashed because &lt;code&gt;index.js&lt;/code&gt; was still trying to import &lt;code&gt;reportWebVitals&lt;/code&gt;. Fixed by removing those import lines from &lt;code&gt;index.js&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Writing the React Component
&lt;/h2&gt;

&lt;p&gt;Here's the complete &lt;code&gt;App.js&lt;/code&gt; I built:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&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&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.css&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="c1"&gt;// State = memory for our component&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;backendStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setBackendStatus&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Checking backend...&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;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUsers&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&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;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// useEffect = runs automatically when page loads&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="nf"&gt;checkBackendHealth&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;// Check if backend is alive&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;checkBackendHealth&lt;/span&gt;&lt;span class="p"&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="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;http://localhost:8000/health&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;data&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;setBackendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`✅ Backend is healthy! Service: &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;service&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="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="nf"&gt;setBackendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;❌ Cannot reach backend. Make sure it&lt;/span&gt;&lt;span class="se"&gt;\'&lt;/span&gt;&lt;span class="s1"&gt;s running on port 8000&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;// Load users from backend&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;loadUsers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setLoading&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="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="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;http://localhost:8000/users&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;data&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;setUsers&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="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="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;Failed to load users:&lt;/span&gt;&lt;span class="dl"&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setLoading&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="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Microservices&lt;/span&gt; &lt;span class="nx"&gt;Platform&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status-card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Backend&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&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;backendStatus&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status-card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Users&lt;/span&gt; &lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loadUsers&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;loading&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;loading&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Loading...&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;Load Users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;users-list&lt;/span&gt;&lt;span class="dl"&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;users&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;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&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="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user-item&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;strong&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/strong&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;br&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;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="p"&gt;))}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&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;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Major Problem: CORS
&lt;/h2&gt;

&lt;p&gt;I ran both servers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend on port 8000 (&lt;code&gt;npm run dev&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Frontend on port 3000 (&lt;code&gt;npm start&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;But my React app showed:&lt;/strong&gt; ❌ Cannot reach backend&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The browser console showed this error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Access to fetch at 'http://localhost:8000/health' from origin 'http://localhost:3000' 
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What is CORS? (Simple Explanation)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CORS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cross-Origin Resource Sharing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Origin&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The domain + port where your app lives (&lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;The Problem&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browsers block requests from one origin to another by default (security)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;The Fix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tell the backend "It's OK to accept requests from React"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;ul&gt;
&lt;li&gt;React = A person at door 3000&lt;/li&gt;
&lt;li&gt;Backend = A person at door 8000&lt;/li&gt;
&lt;li&gt;Browser = Security guard&lt;/li&gt;
&lt;li&gt;Without CORS = Guard says "You can't talk to each other, different doors!"&lt;/li&gt;
&lt;li&gt;With CORS = Guard says "Oh, it's allowed. Go ahead!"&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Fixing CORS (Step by Step)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Install the CORS package
&lt;/h3&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;backend
npm &lt;span class="nb"&gt;install &lt;/span&gt;cors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Add CORS to server.js
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="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;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;cors&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;cors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ← ADD THIS&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="nf"&gt;express&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="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;  &lt;span class="c1"&gt;// ← ADD THIS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Restart both servers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Backend: &lt;code&gt;Ctrl+C&lt;/code&gt; then &lt;code&gt;npm run dev&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Frontend: &lt;code&gt;Ctrl+C&lt;/code&gt; then &lt;code&gt;npm start&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: Hard refresh browser
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Ctrl + Shift + R&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Moment It Worked
&lt;/h2&gt;

&lt;p&gt;After fixing CORS, my browser showed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend Status:&lt;/strong&gt; ✅ Backend is healthy! Service: backend-api&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clicking "Load Users" displayed:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alice (&lt;a href="mailto:alice@example.com"&gt;alice@example.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Bob (&lt;a href="mailto:bob@example.com"&gt;bob@example.com&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Charlie (&lt;a href="mailto:charlie@example.com"&gt;charlie@example.com&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SUCCESS!&lt;/strong&gt; My frontend and backend were finally talking!&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Learnings from Day 3
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Technical Concepts
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;What I Learned&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;React State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;useState&lt;/code&gt; creates memory for your component. When state changes, UI updates automatically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;useEffect&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runs code automatically when component loads (great for fetching initial data)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fetch API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browser's built-in way to make HTTP requests to APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CORS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browser security that blocks cross-origin requests unless backend allows it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ports&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;React uses 3000, backend uses 8000 — different ports = different origins&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Problem-Solving Skills
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reading error messages:&lt;/strong&gt; The CORS error looked scary, but it told me exactly what was wrong&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using browser Console:&lt;/strong&gt; &lt;code&gt;F12&lt;/code&gt; → Console tab shows all errors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing with curl:&lt;/strong&gt; &lt;code&gt;curl http://localhost:8000/health&lt;/code&gt; proved backend was working (curl ignores CORS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Systematic debugging:&lt;/strong&gt; Backend works? Yes. Frontend works? Yes. Connection? CORS issue.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Mistakes I Made (And Fixed)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mistake&lt;/th&gt;
&lt;th&gt;Consequence&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Deleted files without updating imports&lt;/td&gt;
&lt;td&gt;React compilation error&lt;/td&gt;
&lt;td&gt;Removed import lines from index.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Didn't install CORS package&lt;/td&gt;
&lt;td&gt;CORS still blocked&lt;/td&gt;
&lt;td&gt;&lt;code&gt;npm install cors&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forgot to restart backend&lt;/td&gt;
&lt;td&gt;Changes didn't take effect&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Ctrl+C&lt;/code&gt; then &lt;code&gt;npm run dev&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Didn't hard refresh browser&lt;/td&gt;
&lt;td&gt;Saw old cached page&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Ctrl+Shift+R&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Tips for Anyone Building Their First React + API App
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Test backend with curl first&lt;/strong&gt; — If curl works but React doesn't, it's CORS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always restart servers after changes&lt;/strong&gt; — They don't auto-reload config&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use browser Console&lt;/strong&gt; — It's your best friend for debugging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard refresh (Ctrl+Shift+R)&lt;/strong&gt; — Avoid seeing cached old versions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep both terminals visible&lt;/strong&gt; — One for backend, one for frontend&lt;/li&gt;
&lt;/ol&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://react.dev" rel="noopener noreferrer"&gt;React Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" rel="noopener noreferrer"&gt;CORS Explained (MDN)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://expressjs.com/en/resources/middleware/cors.html" rel="noopener noreferrer"&gt;Express CORS Middleware&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Let's Connect!
&lt;/h2&gt;

&lt;p&gt;Have you struggled with CORS before? Building your first full-stack app? I'd love to hear about your journey!&lt;/p&gt;

&lt;p&gt;Drop a comment or connect on LinkedIn. Let's learn together! &lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is Day 3 of my 30-Day Cloud &amp;amp; DevOps Challenge. Follow along as I build a complete microservices platform from scratch!&lt;/em&gt;&lt;/p&gt;



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




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

&lt;/div&gt;

</description>
      <category>devchallenge</category>
      <category>node</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>30-Day Cloud &amp; DevOps Challenge: Day 2 — Building My First Backend API</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Wed, 01 Apr 2026 13:10:35 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-2-building-my-first-backend-api-2ed5</link>
      <guid>https://forem.com/michellewanjiru/30-day-cloud-devops-challenge-day-2-building-my-first-backend-api-2ed5</guid>
      <description>&lt;p&gt;&lt;strong&gt;The journey continues!&lt;/strong&gt; After setting up my project structure on Day 1, today was all about building the heart of my microservices platform: the backend API.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you missed Day 1, you can catch up here: [&lt;a href="https://dev.to/michellewanjiru/day-1-of-my-30-day-cloud-devops-challenge-project-setup-2a5a?trk=public_post_comment-text"&gt;https://dev.to/michellewanjiru/day-1-of-my-30-day-cloud-devops-challenge-project-setup-2a5a?trk=public_post_comment-text&lt;/a&gt;]&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Set Out to Do Today
&lt;/h2&gt;

&lt;p&gt;Build a working REST API that can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Respond to HTTP requests&lt;/li&gt;
&lt;li&gt;Return JSON data&lt;/li&gt;
&lt;li&gt;Serve as the foundation for my microservices platform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Simple, right?&lt;/strong&gt; Well... let me share how it actually went.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Tech Stack I Chose
&lt;/h2&gt;

&lt;p&gt;I decided to go with &lt;strong&gt;Node.js + Express&lt;/strong&gt; for my backend because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript is everywhere (frontend, backend, even DevOps tools)&lt;/li&gt;
&lt;li&gt;Express is lightweight and beginner-friendly&lt;/li&gt;
&lt;li&gt;I already had Node.js installed on my Ubuntu system&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step-by-Step: What I Actually Did
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Initial Setup
&lt;/h3&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;backend
npm init &lt;span class="nt"&gt;-y&lt;/span&gt;  &lt;span class="c"&gt;# Creates package.json&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;express  &lt;span class="c"&gt;# Web framework&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; nodemon  &lt;span class="c"&gt;# Auto-restarts server on changes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What I learned:&lt;/strong&gt; &lt;code&gt;package.json&lt;/code&gt; is like a shopping list for your project. Every tool you add gets listed there, making it easy to share your project with others.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Writing My First Server
&lt;/h3&gt;

&lt;p&gt;I created &lt;code&gt;server.js&lt;/code&gt; with three endpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="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="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;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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&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;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Root endpoint&lt;/span&gt;
&lt;span class="nx"&gt;app&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&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;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;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Welcome to the Microservices Platform API&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&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="c1"&gt;// Health check endpoint&lt;/span&gt;
&lt;span class="nx"&gt;app&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/health&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;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;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;healthy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;backend-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;timestamp&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;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&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;// Users endpoint (mock data for now)&lt;/span&gt;
&lt;span class="nx"&gt;app&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users&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;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="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="mi"&gt;1&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="s1"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;alice@example.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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&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="s1"&gt;Bob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bob@example.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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&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="s1"&gt;Charlie&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;charlie@example.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="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="nf"&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;` Server running on http://localhost:&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;/div&gt;



&lt;p&gt;&lt;strong&gt;Understanding each part:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;app.get()&lt;/code&gt; defines what happens when someone visits a URL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;(req, res) =&amp;gt; {}&lt;/code&gt; is a function that handles the request and sends a response&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;res.json()&lt;/code&gt; sends data back as JSON (the language of APIs)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. The Challenge That Almost Stopped Me
&lt;/h3&gt;

&lt;p&gt;I tried running my server with:&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;&lt;strong&gt;And got this error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm error Missing script: "dev"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;My reaction:&lt;/strong&gt; 😅 &lt;em&gt;Wait, what? I thought I had this!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How I diagnosed it:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run  &lt;span class="c"&gt;# Shows all available scripts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only &lt;code&gt;test&lt;/code&gt; was showing. I had completely forgotten to add the &lt;code&gt;"dev"&lt;/code&gt; script to my package.json!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; I opened &lt;code&gt;package.json&lt;/code&gt; and added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node server.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nodemon server.js"&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;p&gt;&lt;strong&gt;Lesson learned:&lt;/strong&gt; Never assume your configuration is correct. Always verify with simple commands.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. The Moment It Finally Worked
&lt;/h3&gt;

&lt;p&gt;After fixing the script, I ran:&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;&lt;strong&gt;And saw:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; Server running on http://localhost:8000
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in another terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8000/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&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="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Welcome to the Microservices Platform API"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"running"&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;p&gt;&lt;strong&gt;I literally cheered in my chair.🎉&lt;/strong&gt; My first API was alive!&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing All Endpoints
&lt;/h2&gt;

&lt;p&gt;Here's what each endpoint returned:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Response&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Welcome message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /health&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Service status with timestamp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /users&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List of 3 mock users&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All working perfectly!&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Learnings from Day 2
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Technical Concepts I Gained:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;HTTP Methods:&lt;/strong&gt; &lt;code&gt;GET&lt;/code&gt; is how you &lt;em&gt;retrieve&lt;/em&gt; data from a server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON:&lt;/strong&gt; The universal language of APIs — both request and response&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ports:&lt;/strong&gt; Think of them like doors — 8000 is the door my API listens on&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Endpoints:&lt;/strong&gt; Specific URLs that do specific things (&lt;code&gt;/health&lt;/code&gt;, &lt;code&gt;/users&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Package.json scripts:&lt;/strong&gt; Custom commands to automate repetitive tasks&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Problem-Solving Skills:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Diagnosing errors:&lt;/strong&gt; &lt;code&gt;npm run&lt;/code&gt; helped me see what was actually available&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reading error messages:&lt;/strong&gt; "Missing script" told me exactly what was wrong&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step-by-step verification:&lt;/strong&gt; Test each piece before assuming everything works&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Mindset Shifts:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Done is better than perfect:&lt;/strong&gt; My first API doesn't have a database yet, and that's okay&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Celebrate small wins:&lt;/strong&gt; Getting that first curl response was genuinely exciting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embrace mistakes:&lt;/strong&gt; The missing script error taught me more than if everything worked perfectly&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tips for Anyone Starting Their Own API
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start simple.&lt;/strong&gt; My API only does 3 things right now — that's enough!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test as you go.&lt;/strong&gt; Don't write 100 lines of code before testing. Test after each endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use curl or browser.&lt;/strong&gt; It's satisfying to see your API respond in real-time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Save your package.json changes.&lt;/strong&gt; I almost forgot this one!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep notes.&lt;/strong&gt; Document what you learn, especially the mistakes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A Quick Thank You
&lt;/h2&gt;

&lt;p&gt;To everyone following along and the community helping me debug, your support means everything. Special shoutout to those who reminded me to check my package.json scripts!&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next (Day 3)
&lt;/h2&gt;

&lt;p&gt;Tomorrow, I'm building a frontend that will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetch data from my API&lt;/li&gt;
&lt;li&gt;Display the list of users&lt;/li&gt;
&lt;li&gt;Show the health status&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;It's going to be the first time my frontend and backend talk to each other!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Links &amp;amp; Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/Michelle8395/Production-Ready-Microservices-Platform" rel="noopener noreferrer"&gt;Production-Ready-Microservices-Platform&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Express Documentation:&lt;/strong&gt; &lt;a href="https://expressjs.com" rel="noopener noreferrer"&gt;expressjs.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nodemon:&lt;/strong&gt; &lt;a href="https://nodemon.io" rel="noopener noreferrer"&gt;nodemon.io&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Let's Connect!
&lt;/h2&gt;

&lt;p&gt;Are you also learning backend development? Building your first API? I'd love to hear about your journey!&lt;/p&gt;

&lt;p&gt;Drop a comment below or connect with me on LinkedIn at &lt;a href="https://www.linkedin.com/in/michelle-wanjiru-420877275/?lipi=urn%3Ali%3Apage%3Ad_flagship3_detail_base%3BHw%2F74ryNQ7CjXqDkL3k7eQ%3D%3D" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/michelle-wanjiru-420877275/?lipi=urn%3Ali%3Apage%3Ad_flagship3_detail_base%3BHw%2F74ryNQ7CjXqDkL3k7eQ%3D%3D&lt;/a&gt;. Let's learn together! &lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is Day 2 of my 30-Day Cloud &amp;amp; DevOps Challenge. Follow along as I build a complete microservices platform from scratch!&lt;/em&gt;&lt;/p&gt;




</description>
      <category>api</category>
      <category>backend</category>
      <category>devjournal</category>
      <category>node</category>
    </item>
    <item>
      <title>Day 1 of My 30-Day Cloud &amp; DevOps Challenge — Project Setup</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Tue, 31 Mar 2026 08:39:12 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/day-1-of-my-30-day-cloud-devops-challenge-project-setup-2a5a</link>
      <guid>https://forem.com/michellewanjiru/day-1-of-my-30-day-cloud-devops-challenge-project-setup-2a5a</guid>
      <description>&lt;p&gt;Today marks the start of my &lt;strong&gt;30-day Cloud &amp;amp; DevOps challenge&lt;/strong&gt;, and I focused on laying a strong foundation for the project. Before diving into code, I wanted to ensure the structure, documentation, and workflow are clear—because a well-organized project makes everything else much easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Did Today
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Created GitHub repository&lt;/strong&gt; — the central place to track code, documentation, and collaboration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Planned project architecture&lt;/strong&gt; — decided on a three-part system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt; — the user interface&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt; — API and business logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database&lt;/strong&gt; — persistent data storage&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;Created basic folder structure&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  /frontend
  /backend
  /infra       # Terraform configs for cloud infrastructure
  /k8s         # Kubernetes manifests
  /ansible     # Configuration management playbooks
  /jenkins     # CI/CD pipeline scripts
  README.md
  ROADMAP.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this structure?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separates concerns clearly: each component has its own space.&lt;/li&gt;
&lt;li&gt;Makes scaling easier—if I add more microservices, the structure can accommodate them.&lt;/li&gt;
&lt;li&gt;Keeps infrastructure and deployment configurations isolated from application code.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Documentation (README.md, ROADMAP.md) stays at the root for visibility.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Added initial documentation&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;strong&gt;README.md&lt;/strong&gt; — outlines project goal, architecture, tech stack, and future progress.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;ROADMAP.md&lt;/strong&gt; — a detailed 30-day plan that breaks down daily tasks and objectives.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Planned Git branching strategy&lt;/strong&gt;:&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Using conventional commit prefixes for clarity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;feat/&lt;/code&gt; → new features&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fix/&lt;/code&gt; → bug fixes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docs/&lt;/code&gt; → documentation updates&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chore/&lt;/code&gt; → general maintenance tasks&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main&lt;/code&gt; branch — always stable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;develop&lt;/code&gt; branch — integration of features before merging to main&lt;/li&gt;
&lt;li&gt;Feature branches (&lt;code&gt;feat/...&lt;/code&gt;) branch off &lt;code&gt;develop&lt;/code&gt; and merge back via pull requests.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why These Decisions Matter
&lt;/h2&gt;

&lt;p&gt;Starting with a solid structure and clear workflow ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consistency across development&lt;/li&gt;
&lt;li&gt;Easier collaboration if others join&lt;/li&gt;
&lt;li&gt;Smooth CI/CD and deployment setup later&lt;/li&gt;
&lt;li&gt;Traceability of changes through meaningful commits and branches&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Tomorrow, I’ll begin building the &lt;strong&gt;backend API&lt;/strong&gt; and setting up the database. The foundation is ready, now it’s time to code, containerize, and gradually bring the infrastructure to life.&lt;/p&gt;




</description>
      <category>cloud</category>
      <category>devchallenge</category>
      <category>devjournal</category>
      <category>devops</category>
    </item>
    <item>
      <title>Why Branch Naming Conventions Matter More Than You Think</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Wed, 25 Mar 2026 22:07:56 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/why-branch-naming-conventions-matter-more-than-you-think-1g55</link>
      <guid>https://forem.com/michellewanjiru/why-branch-naming-conventions-matter-more-than-you-think-1g55</guid>
      <description>&lt;h2&gt;
  
  
  From final-final-v2 to Something That Makes Sense
&lt;/h2&gt;

&lt;p&gt;I used to name my Git branches like I was improvising under pressure.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;update&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;new-code&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;try-again&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;final-final-v2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the time, it made perfect sense. I knew what I was working on. Until I came back a few days later. Or worse, until someone else had to look at my work.&lt;/p&gt;

&lt;p&gt;That’s when it hit me: &lt;strong&gt;branch naming isn’t just labeling. It’s communication.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In professional software development, poor communication doesn’t just slow you down, it breaks teams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Do Branch Names Even Matter?
&lt;/h2&gt;

&lt;p&gt;A branch name is the first thing people see before they read your code. A good branch name should instantly answer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What&lt;/strong&gt; is being worked on?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why&lt;/strong&gt; does it exist?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is it&lt;/strong&gt; a feature, a fix, or something else?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If someone has to open your code to understand your branch, you’ve already made things harder than necessary. A well-named branch is like a good commit message: it saves time, reduces confusion, and makes collaboration smoother.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Standard Naming Pattern
&lt;/h2&gt;

&lt;p&gt;Most professional teams follow a simple structure:&lt;br&gt;
&lt;code&gt;category/description&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Or, when working with task tracking tools:&lt;br&gt;
&lt;code&gt;category/taskID-description&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This isn’t about being strict for no reason. It’s about &lt;strong&gt;traceability&lt;/strong&gt; and &lt;strong&gt;clarity&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Common Branch Categories
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;feat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;New features&lt;/td&gt;
&lt;td&gt;&lt;code&gt;feat/login-page&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;fix&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bug fixes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fix/header-alignment&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;docs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Documentation changes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docs/update-install-guide&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;refactor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Code improvements (no behavior change)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;refactor/api-auth-logic&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;test&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Adding or fixing tests&lt;/td&gt;
&lt;td&gt;&lt;code&gt;test/ant-movement-logic&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;chore&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Maintenance tasks&lt;/td&gt;
&lt;td&gt;&lt;code&gt;chore/update-go-mod&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;perf&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Performance improvements&lt;/td&gt;
&lt;td&gt;&lt;code&gt;perf/optimize-bfs-search&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ci&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CI/CD configuration changes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ci/github-actions-setup&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Notice something?&lt;/strong&gt; None of them say "stuff," "things," or "updates." Shocking, I know.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Best Practices for Naming Branches
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Use Kebab Case (Hyphens, Not Chaos)
&lt;/h3&gt;

&lt;p&gt;Stick to lowercase and separate words with hyphens.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;feat/add-user-profile&lt;/code&gt; ✔️&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;feat/Add_User_Profile&lt;/code&gt; ❌&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt; It's easier to read, consistent, and plays nicely with CLI tools. Underscores and random capitalization just add friction.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Include Ticket or Task IDs
&lt;/h3&gt;

&lt;p&gt;If you're using Jira, Trello, or GitHub Issues, include the task ID:&lt;br&gt;
&lt;code&gt;fix/LEM-42-invalid-coord-check&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This creates a direct link between the code, the requirement, and the discussion behind it. Future you will be grateful.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Be Descriptive, Not Overdramatic
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bad:&lt;/strong&gt; &lt;code&gt;feat/fix-everything-broken-in-parser-because-life-is-hard&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better:&lt;/strong&gt; &lt;code&gt;feat/parser-ignore-extra-hashes&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Make it clear and concise.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Keep Branches Short-Lived
&lt;/h3&gt;

&lt;p&gt;A branch is not a long-term commitment. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Good workflow:&lt;/strong&gt; Create → Work → Merge → Delete.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bad workflow:&lt;/strong&gt; Create → Forget → Revive → Break → Panic.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  A Simple Workflow Example
&lt;/h2&gt;

&lt;p&gt;Let’s say you’re working on a project like &lt;strong&gt;Lem-in&lt;/strong&gt;. Here’s what clean branch usage looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Implement a new feature&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; feat/bfs-implementation

&lt;span class="c"&gt;# Fix a documentation issue&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; docs/fix-readme-typo

&lt;span class="c"&gt;# Improve performance&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; perf/graph-adjacency-optimization
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without opening a single file, anyone on your team already understands what’s happening. That’s the whole point.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Changed for Me
&lt;/h2&gt;

&lt;p&gt;Learning this didn’t feel like a big deal at first. It’s just naming things, right? But it forced me to think differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be clear about what I’m doing.&lt;/li&gt;
&lt;li&gt;Be intentional with my work.&lt;/li&gt;
&lt;li&gt;Consider the people reading my code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that’s really what good engineering is about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Clean code is important, but &lt;strong&gt;clean communication&lt;/strong&gt; is what makes collaboration work. Branch naming sits at the intersection of organization, teamwork, and maintainability. &lt;/p&gt;

&lt;p&gt;If your branches still look like &lt;code&gt;final-final-v3-this-one-works&lt;/code&gt;, it’s not a Git problem, it’s a discipline problem.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How a Video Travels Across the World to Reach Your Screen</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Sun, 22 Mar 2026 08:05:42 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/how-a-video-travels-across-the-world-to-reach-your-screen-4o3f</link>
      <guid>https://forem.com/michellewanjiru/how-a-video-travels-across-the-world-to-reach-your-screen-4o3f</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;You click on a video.&lt;/p&gt;

&lt;p&gt;It pauses for a fraction of a second, then begins to play as if nothing complicated just happened.&lt;/p&gt;

&lt;p&gt;But in that instant, a chain reaction is triggered across continents, oceans, and machines. Data moves through systems designed with extreme precision, all to deliver a seamless experience.&lt;/p&gt;

&lt;p&gt;This is what actually happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Misconception: Why Satellites Don’t Power the Internet
&lt;/h2&gt;

&lt;p&gt;It’s natural to assume the internet runs through satellites. Space feels like the fastest, most direct route.&lt;/p&gt;

&lt;p&gt;In practice, satellites are rarely used for everyday internet traffic like video streaming.&lt;/p&gt;

&lt;p&gt;The reason is &lt;strong&gt;latency&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A signal sent to a satellite must travel tens of thousands of miles into space and back. That round trip introduces delays that make real-time experiences feel slow and inconsistent.&lt;/p&gt;

&lt;p&gt;Instead, the internet relies on something far more grounded and far more efficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Backbone: Fiber Optic Cables
&lt;/h2&gt;

&lt;p&gt;The internet is physical.&lt;/p&gt;

&lt;p&gt;It exists as a vast network of fiber optic cables laid underground and across ocean floors. These cables transmit data as pulses of light, allowing information to travel at incredible speeds over long distances.&lt;/p&gt;

&lt;p&gt;Entire continents are connected this way.&lt;/p&gt;

&lt;p&gt;When you stream a video, your data is often traveling through these underwater cables quietly carrying information across the planet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Content Lives: Data Centers and Servers
&lt;/h2&gt;

&lt;p&gt;Before a video can reach you, it has to exist somewhere.&lt;/p&gt;

&lt;p&gt;That place is a data center.&lt;/p&gt;

&lt;p&gt;Inside are servers, specialized computers built to store, process, and deliver content at scale. They rely on solid-state drives (SSDs) because speed and reliability are critical.&lt;/p&gt;

&lt;p&gt;When you press play, a server retrieves the video and begins sending it toward your device.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identity on the Internet: IP Addresses and Domain Names
&lt;/h2&gt;

&lt;p&gt;Every device on the internet has a unique identifier called an IP address.&lt;/p&gt;

&lt;p&gt;This is how data knows where to go.&lt;/p&gt;

&lt;p&gt;But raw IP addresses aren’t designed for humans. That’s why domain names exist.&lt;/p&gt;

&lt;p&gt;Instead of remembering something like:&lt;/p&gt;

&lt;p&gt;142.250.190.14&lt;/p&gt;

&lt;p&gt;You use something simple and readable:&lt;/p&gt;

&lt;p&gt;youtube.com&lt;/p&gt;

&lt;p&gt;Domain names are a human-friendly layer on top of machine-level addressing.&lt;/p&gt;

&lt;h2&gt;
  
  
  DNS: Translating Names Into Locations
&lt;/h2&gt;

&lt;p&gt;When you type a domain name, your system needs to find the corresponding IP address.&lt;/p&gt;

&lt;p&gt;This is handled by the Domain Name System (DNS).&lt;/p&gt;

&lt;p&gt;DNS acts like a global lookup service. It takes the name you entered and returns the exact address of the server hosting the content.&lt;/p&gt;

&lt;p&gt;Without DNS, using the internet would mean memorizing long numerical identifiers for every site.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Data Actually Moves: Packets
&lt;/h2&gt;

&lt;p&gt;A video is not sent as a single, continuous file.&lt;/p&gt;

&lt;p&gt;It is broken into smaller pieces called packets.&lt;/p&gt;

&lt;p&gt;[Packet 1] -&amp;gt; [Packet 2] -&amp;gt; [Packet 3] -&amp;gt; ...&lt;/p&gt;

&lt;p&gt;Each packet travels independently across the network and may take a completely different route.&lt;/p&gt;

&lt;p&gt;This approach makes the system faster and more resilient. If one path is congested, packets can be rerouted dynamically.&lt;/p&gt;

&lt;p&gt;When they arrive, your device reassembles them into the original video stream.&lt;/p&gt;

&lt;h2&gt;
  
  
  Routing the Journey: Routers and Signal Conversion
&lt;/h2&gt;

&lt;p&gt;As packets move through the network, they pass through routers.&lt;/p&gt;

&lt;p&gt;Routers determine the most efficient path for each packet in real time, constantly adapting to network conditions.&lt;/p&gt;

&lt;p&gt;They also handle signal conversion.&lt;/p&gt;

&lt;p&gt;Fiber optic cables carry data as light. When that data reaches your local network, routers convert it into electrical signals that your devices can understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Keeps Order: ICANN
&lt;/h2&gt;

&lt;p&gt;With billions of devices connected globally, coordination is essential.&lt;/p&gt;

&lt;p&gt;This responsibility falls to ICANN (Internet Corporation for Assigned Names and Numbers).&lt;/p&gt;

&lt;p&gt;ICANN ensures that:&lt;/p&gt;

&lt;p&gt;Every domain name is unique&lt;br&gt;
IP addresses are properly allocated&lt;br&gt;
The system remains globally consistent&lt;/p&gt;

&lt;p&gt;Without this level of coordination, the internet would quickly become unreliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Rules That Make It Work: Protocols
&lt;/h2&gt;

&lt;p&gt;None of this works without a shared set of rules.&lt;/p&gt;

&lt;p&gt;These rules are called protocols.&lt;/p&gt;

&lt;p&gt;They define how data is:&lt;/p&gt;

&lt;p&gt;Broken into packets&lt;br&gt;
Transmitted across networks&lt;br&gt;
Reassembled at its destination&lt;/p&gt;

&lt;p&gt;Protocols ensure that systems built in different parts of the world can communicate without conflict.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;The internet feels instant, but it is anything but simple.&lt;/p&gt;

&lt;p&gt;Every video you watch depends on:&lt;/p&gt;

&lt;p&gt;Physical infrastructure spanning the globe&lt;br&gt;
Systems for identifying and locating devices&lt;br&gt;
Rules governing how data is transmitted&lt;br&gt;
Machines working continuously behind the scenes&lt;/p&gt;

&lt;p&gt;All of this happens in seconds, often without you noticing.&lt;/p&gt;

&lt;p&gt;That's why streaming a video feels effortless.&lt;/p&gt;

&lt;p&gt;But behind that simplicity is a deeply coordinated system of infrastructure, computation, and rules working together in real time.&lt;/p&gt;

&lt;p&gt;The next time you press play, consider what just happened.&lt;/p&gt;

&lt;p&gt;A request left your device, crossed continents, located the right server, and returned as a perfectly assembled stream of data&lt;/p&gt;

&lt;p&gt;All in the time it took you to blink.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>computerscience</category>
      <category>networking</category>
      <category>web</category>
    </item>
    <item>
      <title>GitHub Workflows and CI/CD: The System Behind Every Functional Team</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Sun, 22 Mar 2026 07:41:17 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/github-workflows-and-cicd-the-system-behind-every-functional-team-5b11</link>
      <guid>https://forem.com/michellewanjiru/github-workflows-and-cicd-the-system-behind-every-functional-team-5b11</guid>
      <description>&lt;h3&gt;
  
  
  Code Isn’t the Problem
&lt;/h3&gt;

&lt;p&gt;Most developers think the hard part is writing code.&lt;/p&gt;

&lt;p&gt;It’s not.&lt;/p&gt;

&lt;p&gt;The real difficulty shows up the moment you’re not working alone.&lt;/p&gt;

&lt;p&gt;Multiple people pushing changes. Features colliding. Bugs appearing out of nowhere.&lt;/p&gt;

&lt;p&gt;Suddenly, it’s not about what you build.&lt;/p&gt;

&lt;p&gt;It’s about how changes move through your system.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Reality: Code Needs a Process
&lt;/h3&gt;

&lt;p&gt;In a team, code doesn’t just exist.&lt;/p&gt;

&lt;p&gt;It has a lifecycle.&lt;/p&gt;

&lt;p&gt;It’s introduced. Tested. Reviewed. Merged. Deployed.&lt;/p&gt;

&lt;p&gt;Without structure, that lifecycle breaks.&lt;/p&gt;

&lt;p&gt;That’s where GitHub workflows and CI/CD pipelines come in.&lt;/p&gt;




&lt;h3&gt;
  
  
  GitHub Workflows: Controlling the Chaos
&lt;/h3&gt;

&lt;p&gt;A GitHub workflow isn’t just about branches.&lt;/p&gt;

&lt;p&gt;It’s a system for managing change safely.&lt;/p&gt;

&lt;p&gt;A simple structure most teams use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main&lt;/code&gt; → production-ready code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;develop&lt;/code&gt; → integration branch&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;feature/*&lt;/code&gt; → new work&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fix/*&lt;/code&gt; → bug fixes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example branch names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;feature/payment-integration
fix/api-timeout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates clarity.&lt;/p&gt;

&lt;p&gt;You know what someone is working on without opening the code.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Flow: What Happens to Every Change
&lt;/h3&gt;

&lt;p&gt;Every change follows a path:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a branch from &lt;code&gt;develop&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Build your feature&lt;/li&gt;
&lt;li&gt;Push your code&lt;/li&gt;
&lt;li&gt;Open a Pull Request&lt;/li&gt;
&lt;li&gt;Run automated checks&lt;/li&gt;
&lt;li&gt;Get reviewed&lt;/li&gt;
&lt;li&gt;Merge into &lt;code&gt;develop&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Deploy through &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This isn’t bureaucracy.&lt;/p&gt;

&lt;p&gt;It’s protection against chaos.&lt;/p&gt;




&lt;h3&gt;
  
  
  CI/CD: Where Things Become Automatic
&lt;/h3&gt;

&lt;p&gt;Workflows define how things should happen.&lt;/p&gt;

&lt;p&gt;CI/CD makes sure they actually do.&lt;/p&gt;

&lt;p&gt;Continuous Integration means every time you push code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests run&lt;/li&gt;
&lt;li&gt;Code builds&lt;/li&gt;
&lt;li&gt;Errors are caught early&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Continuous Deployment means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Approved code is deployed automatically&lt;/li&gt;
&lt;li&gt;No manual steps&lt;/li&gt;
&lt;li&gt;No guesswork&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Example: GitHub Actions Pipeline (Go)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;name: Go CI Pipeline

on:
  push:
    branches: [ "develop", "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  test-build:
    runs-on: ubuntu-latest&lt;span class="sb"&gt;

    steps:
      - uses: actions/checkout@v3

      - uses: actions/setup-go@v4
        with:
          go-version: '1.21'

      - run: go mod tidy
      - run: go test ./...
      - run: go build ./...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Without CI/CD:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Testing depends on humans&lt;/li&gt;
&lt;li&gt;Bugs reach production&lt;/li&gt;
&lt;li&gt;Deployments feel risky&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With CI/CD:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every change is verified&lt;/li&gt;
&lt;li&gt;Teams move faster with confidence&lt;/li&gt;
&lt;li&gt;Collaboration becomes smoother&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Where we Go Wrong
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;No branch naming convention → confusion&lt;/li&gt;
&lt;li&gt;Skipping pull requests → no review&lt;/li&gt;
&lt;li&gt;No automated tests → CI is useless&lt;/li&gt;
&lt;li&gt;Overcomplicating pipelines → everything slows down&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;GitHub workflows and CI/CD are not advanced topics.&lt;/p&gt;

&lt;p&gt;They are the foundation of working with others.&lt;/p&gt;

&lt;p&gt;Code matters.&lt;/p&gt;

&lt;p&gt;But the system around your code is what makes everything actually work.&lt;/p&gt;




</description>
    </item>
    <item>
      <title>🧵 Goroutines, Channels, and the Quiet Beauty of Concurrency in Go</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Fri, 20 Mar 2026 17:10:27 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/goroutines-channels-and-the-quiet-beauty-of-concurrency-in-go-3jpf</link>
      <guid>https://forem.com/michellewanjiru/goroutines-channels-and-the-quiet-beauty-of-concurrency-in-go-3jpf</guid>
      <description>&lt;p&gt;There’s a moment every Go developer hits.&lt;/p&gt;

&lt;p&gt;At first, goroutines feel like magic,  you sprinkle &lt;code&gt;go&lt;/code&gt; in front of a function and suddenly things run &lt;em&gt;at the same time&lt;/em&gt;. Fast. Clean. Almost too easy.&lt;/p&gt;

&lt;p&gt;And then… confusion creeps in.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Why is this value wrong?”&lt;/li&gt;
&lt;li&gt;“Why is my program stuck?”&lt;/li&gt;
&lt;li&gt;“Why does this work sometimes… but not always?”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s when you realize:&lt;br&gt;&lt;br&gt;
Go’s concurrency model isn’t just about running things at once, it’s about &lt;strong&gt;coordination&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s walk through it like humans, not textbooks.&lt;/p&gt;


&lt;h2&gt;
  
  
  Goroutines: Tiny Workers With a Job
&lt;/h2&gt;

&lt;p&gt;Think of goroutines as tiny workers you can spin up instantly.&lt;/p&gt;

&lt;p&gt;You don’t manage them. You don’t schedule them.&lt;br&gt;&lt;br&gt;
You just give them work and let Go figure out the rest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;processImage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each of these runs independently.&lt;/p&gt;

&lt;p&gt;What makes goroutines special is not just that they’re concurrent, it’s that they’re &lt;strong&gt;lightweight enough to feel disposable&lt;/strong&gt;. You stop worrying about “cost” and start thinking in terms of &lt;em&gt;tasks&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Subtle Truth: Concurrency Is Coordination
&lt;/h2&gt;

&lt;p&gt;A lot of people think concurrency is about speed.&lt;/p&gt;

&lt;p&gt;It’s not.&lt;/p&gt;

&lt;p&gt;It’s about &lt;strong&gt;structuring your program so multiple things can happen without stepping on each other&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Speed is just a side effect.&lt;/p&gt;




&lt;h2&gt;
  
  
  Channels: Conversations, Not Pipes
&lt;/h2&gt;

&lt;p&gt;Now imagine those goroutines as people in a room.&lt;/p&gt;

&lt;p&gt;If they all start talking at once without structure, it’s chaos.&lt;/p&gt;

&lt;p&gt;Channels bring order.&lt;/p&gt;

&lt;p&gt;They’re not just data pipes, they’re &lt;strong&gt;conversations with rules&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you send:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="s"&gt;"done"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And receive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Something deeper is happening:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One goroutine is saying: “I’m ready to hand this off.”&lt;br&gt;&lt;br&gt;
Another is saying: “I’m ready to receive it.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They meet &lt;em&gt;at that exact moment&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Magic of Blocking
&lt;/h2&gt;

&lt;p&gt;Here’s where Go does something beautiful.&lt;/p&gt;

&lt;p&gt;Channels &lt;strong&gt;block by default&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you send and no one is receiving → you wait&lt;/li&gt;
&lt;li&gt;If you receive and no one has sent → you wait&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No extra code. No explicit synchronization.&lt;/p&gt;

&lt;p&gt;It’s like a handshake, both sides have to be ready.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Small, Human Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// doing some work...&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="s"&gt;"I'm done"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&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;There’s no explicit “wait” here.&lt;/p&gt;

&lt;p&gt;But &lt;code&gt;main&lt;/code&gt; &lt;em&gt;waits anyway&lt;/em&gt; — because it’s listening.&lt;/p&gt;

&lt;p&gt;That’s synchronization, quietly happening under the hood.&lt;/p&gt;




&lt;h2&gt;
  
  
  Buffered Channels: When Timing Doesn’t Have to Match
&lt;/h2&gt;

&lt;p&gt;Real life isn’t always a perfect handshake.&lt;/p&gt;

&lt;p&gt;Sometimes you leave a message and walk away.&lt;/p&gt;

&lt;p&gt;That’s what buffered channels are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the sender doesn’t immediately block.&lt;/p&gt;

&lt;p&gt;It’s like dropping letters into a mailbox, as long as there’s space, you don’t have to wait.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Sharing Becomes Inevitable
&lt;/h2&gt;

&lt;p&gt;So far, everything feels clean.&lt;/p&gt;

&lt;p&gt;Goroutines talk through channels. No shared state. No problems.&lt;/p&gt;

&lt;p&gt;But in real systems, you &lt;em&gt;will&lt;/em&gt; end up sharing data.&lt;/p&gt;

&lt;p&gt;And that’s where things get dangerous.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Race Conditions
&lt;/h2&gt;

&lt;p&gt;Consider this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks harmless.&lt;/p&gt;

&lt;p&gt;But under the hood, &lt;code&gt;counter++&lt;/code&gt; is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read the value&lt;/li&gt;
&lt;li&gt;Add 1&lt;/li&gt;
&lt;li&gt;Write it back&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now imagine two goroutines doing this at the same time.&lt;/p&gt;

&lt;p&gt;They can step on each other, and suddenly your count is wrong.&lt;/p&gt;

&lt;p&gt;Not always. Just enough to make debugging painful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mutex: A Simple but Powerful Guard
&lt;/h2&gt;

&lt;p&gt;A mutex is not fancy.&lt;/p&gt;

&lt;p&gt;It just says: &lt;strong&gt;“One at a time.”&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mu&lt;/span&gt; &lt;span class="n"&gt;sync&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mutex&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
    &lt;span class="n"&gt;mu&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unlock&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;Now, no matter how many goroutines call &lt;code&gt;increment&lt;/code&gt;, only one can modify &lt;code&gt;counter&lt;/code&gt; at once.&lt;/p&gt;

&lt;p&gt;It’s like a single key to a locked room, whoever holds it gets exclusive access.&lt;/p&gt;




&lt;h2&gt;
  
  
  Channels vs Mutex: A Practical Way to Think About It
&lt;/h2&gt;

&lt;p&gt;This is where many developers get stuck.&lt;/p&gt;

&lt;p&gt;So here’s a grounded way to decide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you’re &lt;strong&gt;passing data between goroutines&lt;/strong&gt; → use channels&lt;/li&gt;
&lt;li&gt;If you’re &lt;strong&gt;protecting shared data&lt;/strong&gt; → use a mutex&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or even simpler:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use channels when you can.&lt;br&gt;&lt;br&gt;
Use mutexes when you must.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What’s Really Happening Under the Hood
&lt;/h2&gt;

&lt;p&gt;When you write concurrent Go code, you’re designing a system of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Independent workers (goroutines)&lt;/li&gt;
&lt;li&gt;Communication paths (channels)&lt;/li&gt;
&lt;li&gt;Safety boundaries (mutexes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of these are missing or misused:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You get deadlocks (everything waits forever)&lt;/li&gt;
&lt;li&gt;Or race conditions (things break unpredictably)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Mindset Shift
&lt;/h2&gt;

&lt;p&gt;The real shift isn’t technical, it’s mental.&lt;/p&gt;

&lt;p&gt;You stop thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“How do I make this faster?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And start thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“How do I let these pieces work independently, but safely?”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Try This Yourself
&lt;/h2&gt;

&lt;p&gt;A simple exercise:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Spin up multiple goroutines&lt;/li&gt;
&lt;li&gt;Let each send a number into a channel&lt;/li&gt;
&lt;li&gt;Collect and sum them in &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then try the same thing using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a shared variable&lt;/li&gt;
&lt;li&gt;a mutex&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll notice something subtle:&lt;/p&gt;

&lt;p&gt;Channels feel like coordination.&lt;br&gt;&lt;br&gt;
Mutexes feel like control.&lt;/p&gt;

&lt;p&gt;Both are useful, but they &lt;em&gt;feel different&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;Go’s concurrency model isn’t just a tool, it’s a philosophy.&lt;/p&gt;

&lt;p&gt;It nudges you toward writing programs that don’t just &lt;em&gt;run&lt;/em&gt;…&lt;/p&gt;

&lt;p&gt;…but programs where things &lt;strong&gt;move, communicate, and flow together&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And once that clicks, concurrency stops being scary&lt;br&gt;&lt;br&gt;
and starts being something you can actually enjoy.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Dart async/await Function: What Your App Is Actually Doing When It Says “Loading…”</title>
      <dc:creator>Michelle </dc:creator>
      <pubDate>Mon, 09 Feb 2026 07:53:13 +0000</pubDate>
      <link>https://forem.com/michellewanjiru/the-dart-asyncawait-function-what-your-app-is-actually-doing-when-it-says-loading-2mgh</link>
      <guid>https://forem.com/michellewanjiru/the-dart-asyncawait-function-what-your-app-is-actually-doing-when-it-says-loading-2mgh</guid>
      <description>&lt;p&gt;For a long time, I thought “loading” meant my app was stuck.&lt;/p&gt;

&lt;p&gt;Not broken. Just frozen. Waiting. Politely doing nothing until the data finally showed up.&lt;/p&gt;

&lt;p&gt;That idea lived in my head every time I saw a loading spinner. I didn’t question it. I accepted that this was how apps worked and hoped for the best.&lt;/p&gt;

&lt;p&gt;Then one day, it stopped making sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Picture I Had in My Head
&lt;/h2&gt;

&lt;p&gt;I imagined my Dart app working like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run some code&lt;/li&gt;
&lt;li&gt;Hit an &lt;code&gt;await&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Everything stops&lt;/li&gt;
&lt;li&gt;The app waits&lt;/li&gt;
&lt;li&gt;Data arrives&lt;/li&gt;
&lt;li&gt;The app continues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simple. Linear. Comforting.&lt;/p&gt;

&lt;p&gt;Except real apps don’t behave like that. And once you notice it, you can’t unsee it.&lt;/p&gt;

&lt;p&gt;Buttons still respond. Animations still move. Logs still appear. The app is clearly alive.&lt;/p&gt;

&lt;p&gt;So what exactly is “waiting”?&lt;/p&gt;

&lt;h2&gt;
  
  
  “Loading” Is a Friendly Lie
&lt;/h2&gt;

&lt;p&gt;When an app shows “Loading…”, it’s not describing what the app is doing.&lt;/p&gt;

&lt;p&gt;It’s describing what &lt;em&gt;you&lt;/em&gt; are waiting for.&lt;/p&gt;

&lt;p&gt;Behind the scenes, your Dart app is still running. It just refuses to block itself for one slow operation like a network request or a database call.&lt;/p&gt;

&lt;p&gt;Instead, it quietly says:&lt;/p&gt;

&lt;p&gt;“I’ll come back to this later.”&lt;/p&gt;

&lt;p&gt;And then it keeps going.&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; Really Mean in Dart
&lt;/h2&gt;

&lt;p&gt;This was the moment things clicked for me.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;await&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; pause the entire program.&lt;/p&gt;

&lt;p&gt;It pauses &lt;strong&gt;one function&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;p&gt;When a Dart function hits an &lt;code&gt;await&lt;/code&gt;, it steps aside. The event loop keeps running. Other tasks continue. UI updates still happen. The app stays responsive.&lt;/p&gt;

&lt;p&gt;The future you’re waiting on finishes when it finishes, and Dart calmly resumes that function later.&lt;/p&gt;

&lt;p&gt;No freezing.&lt;br&gt;
No global pause.&lt;br&gt;
No drama.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Was So Confusing at First
&lt;/h2&gt;

&lt;p&gt;As beginners, we think in steps.&lt;/p&gt;

&lt;p&gt;Line 1 runs.&lt;br&gt;
Line 2 runs.&lt;br&gt;
Line 3 runs.&lt;/p&gt;

&lt;p&gt;Async code breaks that mental model.&lt;/p&gt;

&lt;p&gt;Now things start, pause, resume, and overlap. Not randomly, but not in a straight line either.&lt;/p&gt;

&lt;p&gt;Once I stopped forcing async code into a linear story, it stopped feeling mysterious.&lt;/p&gt;

&lt;p&gt;It wasn’t magic.&lt;/p&gt;

&lt;p&gt;It was scheduling.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Meaning of “Loading…”
&lt;/h2&gt;

&lt;p&gt;Now when I see a loading spinner, I read it differently.&lt;/p&gt;

&lt;p&gt;It doesn’t mean:&lt;br&gt;
“Nothing is happening.”&lt;/p&gt;

&lt;p&gt;It means:&lt;br&gt;
“Something is happening somewhere else, and we’ll let you know when it’s done.”&lt;/p&gt;

&lt;p&gt;That small shift changed how I debug, how I read logs, and how I write async functions in Dart.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;If you’re working with Dart, Flutter, or any modern framework, async is everywhere.&lt;/p&gt;

&lt;p&gt;Most mistakes don’t come from syntax errors. They come from the wrong mental picture.&lt;/p&gt;

&lt;p&gt;Once you understand that your app is always running and &lt;code&gt;await&lt;/code&gt; is just a polite pause, things feel calmer. Clearer. Less fragile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;In the end, I realized my app was never frozen.&lt;/p&gt;

&lt;p&gt;It was busy.&lt;/p&gt;

&lt;p&gt;It just didn’t think I needed all the details.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>flutter</category>
      <category>learning</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
