<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Abdullah Sheikh</title>
    <description>The latest articles on DEV Community by Abdullah Sheikh (@-abdullah-sheikh).</description>
    <link>https://dev.to/-abdullah-sheikh</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%2F2424603%2F84b1361e-a885-4af1-94d9-83088573135b.png</url>
      <title>DEV Community: Abdullah Sheikh</title>
      <link>https://dev.to/-abdullah-sheikh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/-abdullah-sheikh"/>
    <language>en</language>
    <item>
      <title>How to Set Up a Self-Hosted Server on Oracle Cloud Free Tier in 30 Minutes</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Wed, 20 May 2026 12:06:45 +0000</pubDate>
      <link>https://dev.to/-abdullah-sheikh/how-to-set-up-a-self-hosted-server-on-oracle-cloud-free-tier-in-30-minutes-1nh5</link>
      <guid>https://dev.to/-abdullah-sheikh/how-to-set-up-a-self-hosted-server-on-oracle-cloud-free-tier-in-30-minutes-1nh5</guid>
      <description>&lt;p&gt;&lt;em&gt;Step‑by‑step guide to launch, configure, and secure a Linux server on Oracle Cloud’s free tier&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start: What You'll Walk Away With
&lt;/h2&gt;

&lt;p&gt;By the end of this guide you’ll be running a live Linux VM on Oracle Cloud’s free tier without spending a dime.&lt;/p&gt;

&lt;p&gt;Think of the process like ordering a pizza: you pick the crust (the VM), add the toppings (SSH access and web stack), then make sure it arrives hot and safe (firewall and TLS).&lt;/p&gt;

&lt;p&gt;First you’ll spin up a &lt;strong&gt;free Oracle Cloud VM&lt;/strong&gt; and lock down a secure &lt;code&gt;ssh&lt;/code&gt; connection, just like getting a key to your new apartment.&lt;/p&gt;

&lt;p&gt;Next you’ll set up a web server—Apache or Nginx—so you can drop a simple HTML file and see it show up, similar to placing a flyer on a billboard.&lt;/p&gt;

&lt;p&gt;Finally you’ll tighten security with basic firewall rules and a free TLS certificate, giving your site the same protection a hotel safe provides for valuables.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Launch the free tier instance and note its public IP.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure &lt;code&gt;ssh&lt;/code&gt; keys and connect from your terminal.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install Apache or Nginx, start the service, and serve a test page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open only needed ports with &lt;code&gt;iptables&lt;/code&gt; or &lt;code&gt;ufw&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Obtain a Let’s Encrypt certificate and enable HTTPS.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tools:&lt;/strong&gt; Oracle Cloud Console, &lt;code&gt;ssh&lt;/code&gt;, &lt;code&gt;apt&lt;/code&gt;/&lt;code&gt;yum&lt;/code&gt;, &lt;code&gt;certbot&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Keep the VM in the free tier by staying under the 2 CPU/200 GB storage limit.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt; ssh -i ~/.ssh/oracle_key ubuntu@&lt;em&gt;your_ip&lt;/em&gt; to connect.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you have a production‑ready server you can actually use, not just a virtual box that sits idle.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Self‑Hosted Server on Oracle Cloud Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;Imagine you’ve just booked a tiny, fully furnished studio on a short‑term lease. The walls are already there, the electricity works, and you get the keys without signing a long contract. That studio is your &lt;strong&gt;virtual machine&lt;/strong&gt; on Oracle Cloud’s free tier.&lt;/p&gt;

&lt;p&gt;Just like you can paint the walls, hang pictures, or bring in a couch, you can install any Linux distro, add a web server, or deploy a database. Nothing stops you from rearranging the furniture (changing configurations) or even kicking out a roommate (removing software) whenever you want.&lt;/p&gt;

&lt;p&gt;The big difference from a traditional rental is the landlord never asks for rent as long as you stay within the free limits. Oracle provides the underlying hardware, power, and network, but the VM’s interior belongs entirely to you.&lt;/p&gt;

&lt;p&gt;Think of the setup process like moving into that studio:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Pick a floor plan (choose an instance shape).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Get the keys (create the VM).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Turn on the lights and plug in your laptop (connect via SSH).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you’re inside, you control every switch. Want to run a Node.js API? Install Node. Need a MySQL database? Spin it up. All of that happens on your &lt;strong&gt;oracle cloud free tier server&lt;/strong&gt;, and you won’t see a surprise bill if you keep an eye on usage.&lt;/p&gt;

&lt;p&gt;In short, a self‑hosted server on Oracle Cloud is just a personal, no‑rent apartment in the cloud, ready for you to furnish however you like.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 4 Mistakes Everyone Makes With Oracle Cloud Free Tier
&lt;/h2&gt;

&lt;p&gt;Most people hit a wall on the free tier because they skip the little details that keep the service truly free.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Forgetting to set the correct shape limits&lt;/strong&gt; – It’s like ordering a pizza without checking the slice count; you think you’re getting a small, but the system hands you a larger, billable size. In the console, lock the VM shape to &lt;code&gt;VM.Standard.E2.1.Micro&lt;/code&gt; and verify the &lt;code&gt;Monthly Free Usage Limit&lt;/code&gt; under “Limits &amp;amp; Quotas”.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using the default “oraclecloud” SSH key&lt;/strong&gt; – Handing out a master key to every guest is a security nightmare. Generate a fresh key pair (&lt;code&gt;ssh-keygen -t rsa -b 4096 -f mykey&lt;/code&gt;), upload the public part, and delete the default key from the instance’s &lt;em&gt;Metadata&lt;/em&gt; page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Skipping VCN/subnet firewall rules&lt;/strong&gt; – Imagine a house with all doors locked; no one can get in, even you. Open ports 22 (SSH) and 80/443 (web) in the VCN security list, or create a custom security group that mirrors the traffic your app needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ignoring the automatic shutdown policy&lt;/strong&gt; – Leaving a car idling wastes fuel; leaving a VM running wastes free hours. Enable &lt;code&gt;Auto‑Terminate&lt;/code&gt; after 24 hours of inactivity or set a cron job that runs &lt;code&gt;oci compute instance-action --action STOP&lt;/code&gt; on a schedule.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat sheet&lt;/strong&gt;: shape = &lt;code&gt;VM.Standard.E2.1.Micro&lt;/code&gt;, SSH key = personal, firewall = ports 22 / 80 / 443, shutdown = auto‑terminate or cron.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep these four checkpoints in mind and the Oracle cloud free tier server stays free.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Set Up a Self‑Hosted Server on Oracle Cloud: Step‑by‑Step
&lt;/h2&gt;

&lt;p&gt;Grab your laptop, fire up a browser, and follow these actions one after another.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Sign up for the &lt;strong&gt;Oracle Cloud Free Tier&lt;/strong&gt; and verify the email and phone you provided. Think of it like registering for a loyalty card before you can collect points.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the OCI Console, click &lt;em&gt;Create a Compute Instance&lt;/em&gt;. Choose the &lt;strong&gt;‘Always Free’ VM.Standard.E2.1.Micro&lt;/strong&gt; shape – it’s the free‑tier version of a small rental car.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set up networking: create a VCN, add a public subnet, then open ports 22, 80, 443. This is like drawing a map, marking the road (subnet) and opening the gates (ports) you’ll need.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On your workstation generate an SSH key pair:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-b&lt;/span&gt; 4096 &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.ssh/oracle_key &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the contents of &lt;code&gt;~/.ssh/oracle_key.pub&lt;/code&gt; into the instance’s SSH key field. It’s the digital equivalent of handing over a house key before you move in.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Launch the instance and note the public IP address that appears. Treat that IP like the street address of your new place.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open a terminal and connect:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/oracle_key opc@
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Update the OS packages:
&lt;/li&gt;
&lt;/ul&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;dnf update &lt;span class="nt"&gt;-y&lt;/span&gt;   &lt;span class="c"&gt;# for Oracle Linux&lt;/span&gt;
&lt;span class="c"&gt;# or&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="nt"&gt;-y&lt;/span&gt;   &lt;span class="c"&gt;# for Ubuntu&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Install Nginx:
&lt;/li&gt;
&lt;/ul&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;dnf &lt;span class="nb"&gt;install &lt;/span&gt;nginx &lt;span class="nt"&gt;-y&lt;/span&gt;   &lt;span class="c"&gt;# Oracle Linux&lt;/span&gt;
&lt;span class="c"&gt;# or&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;nginx &lt;span class="nt"&gt;-y&lt;/span&gt;   &lt;span class="c"&gt;# Ubuntu&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Enable and start the web server so it survives reboots:
&lt;/li&gt;
&lt;/ul&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;systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; &lt;span class="nt"&gt;--now&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Harden the box:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure a firewall (&lt;code&gt;firewalld&lt;/code&gt; on Oracle Linux or &lt;code&gt;ufw&lt;/code&gt; on Ubuntu) to allow only 22, 80, 443.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Disable root SSH login by setting &lt;code&gt;PermitRootLogin no&lt;/code&gt; in &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; and restarting SSH.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install Certbot for a free HTTPS certificate:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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;dnf &lt;span class="nb"&gt;install &lt;/span&gt;certbot python3-certbot-nginx &lt;span class="nt"&gt;-y&lt;/span&gt;   &lt;span class="c"&gt;# Oracle Linux&lt;/span&gt;
&lt;span class="c"&gt;# or&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;certbot python3-certbot-nginx &lt;span class="nt"&gt;-y&lt;/span&gt;   &lt;span class="c"&gt;# Ubuntu&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;sudo certbot --nginx&lt;/code&gt; and follow the prompts – it’s like ordering a locked door for your website.&lt;/p&gt;

&lt;p&gt;Now your oracle cloud free tier server is up, secure, and ready to host.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example: Deploying a Portfolio Site for Maya the Designer
&lt;/h2&gt;

&lt;p&gt;Maya signs up for a free Oracle account, picks the same VM shape, and names the instance &lt;strong&gt;maya‑portfolio&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;She connects via SSH using the steps 1‑6, then runs:&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;dnf &lt;span class="nb"&gt;install &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, she copies her &lt;code&gt;index.html&lt;/code&gt; to the web root with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scp index.html user@public_ip:/usr/share/nginx/html/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To secure the site, Maya runs:&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;certbot &lt;span class="nt"&gt;--nginx&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; portfolio.maya.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of the process like ordering a meal: you pick the dish (VM), add the sauce (nginx), place the garnish (HTML), and finish with a sweet dessert (HTTPS certificate).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Verify the firewall allows ports 80 and 443; Oracle Cloud’s default security list blocks them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; After the certbot step, reload Nginx with &lt;code&gt;sudo systemctl reload nginx&lt;/code&gt; to apply the new cert.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Test the site with &lt;code&gt;curl -I https://portfolio.maya.com&lt;/code&gt; to confirm a 200 response.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now Maya’s portfolio is live at &lt;a href="https://portfolio.maya.com" rel="noopener noreferrer"&gt;https://portfolio.maya.com&lt;/a&gt;, fully HTTPS‑secured, all thanks to an &lt;em&gt;oracle cloud free tier server&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools That Make This Easier
&lt;/h2&gt;

&lt;p&gt;Grab the tools that turn a raw VM into a ready‑to‑run server, just like picking the right kitchen gadgets makes a dinner prep painless.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Oracle Cloud Console (2025 UI)&lt;/strong&gt; – The control panel where you spin up instances, attach VNICs, and set security lists. Think of it as Google Maps for your cloud: you plot the route, then follow the directions without getting lost.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PuTTY / Termius&lt;/strong&gt; – Cross‑platform SSH clients that give you a terminal window into the VM. It’s like ordering a meal and having the server bring it straight to your table, no matter which restaurant (Windows, macOS, Linux) you’re at.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Visual Studio Code + Remote‑SSH&lt;/strong&gt; – Open the VM’s filesystem in your favorite editor and save changes instantly. Imagine packing a suitcase: you load items directly into the bag without having to unpack and repack later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Certbot 2.7&lt;/strong&gt; – The official Let’s Encrypt client that fetches and renews SSL certificates with a single command. It works like a vending machine that dispenses a fresh, valid certificate every 90 days without you pressing any extra buttons.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;UFW (Ubuntu) or firewalld (Oracle Linux)&lt;/strong&gt; – Simple firewall front‑ends for opening only the ports you need. They act like a doorman who checks IDs before letting anyone into the building.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connect: ssh -i mykey.pem opc@&lt;em&gt;public_ip&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install Certbot: &lt;code&gt;sudo snap install --classic certbot&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open HTTP/HTTPS: &lt;code&gt;sudo ufw allow 80,443/tcp&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these five tools in hand, the rest of the setup is just a matter of following the steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: Oracle Cloud Free Tier Server Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Grab this cheat sheet when you need a quick refresher—no fluff, just the moves.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Sign up, verify email, and open the Oracle Cloud Console. Think of it like ordering a coffee: you create an account, confirm your payment method, then you’re ready to pick a drink.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a VM: choose &lt;strong&gt;Always Free&lt;/strong&gt; → &lt;strong&gt;VM.Standard.E2.1.Micro&lt;/strong&gt;. It’s the “basic espresso” of cloud instances—nothing fancy, but it gets the job done.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set up networking: add a VCN, attach a public subnet, and open ports &lt;code&gt;22&lt;/code&gt;, &lt;code&gt;80&lt;/code&gt;, &lt;code&gt;443&lt;/code&gt;. Imagine drawing a route on Google Maps; you need the right roads open to reach your destination.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Generate an SSH key pair, paste the public key into the console, then launch. This is like packing a suitcase with a lock: the key lets you open it later.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SSH into the instance and run the initial setup. For example, &lt;em&gt;Alex&lt;/em&gt;, a freelance developer, types:&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;dnf update &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then starts Nginx, adjusts the firewall, and adds Certbot for HTTPS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable the services, configure the firewall, and obtain a free TLS cert. It’s the same as setting up a home security system after you’ve moved in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test the deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; http://
curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see &lt;code&gt;200 OK&lt;/code&gt;, you’re live.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sign‑up &amp;amp; Console&lt;/strong&gt;: Register, verify, open console.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VM Selection&lt;/strong&gt;: Always Free → VM.Standard.E2.1.Micro.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Networking&lt;/strong&gt;: VCN + Public Subnet, open 22/80/443.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSH Keys&lt;/strong&gt;: Create, paste public key, launch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Initial Setup&lt;/strong&gt;: &lt;code&gt;sudo dnf update&lt;/code&gt;, install Nginx, enable service.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Security&lt;/strong&gt;: Firewall rules, Certbot HTTPS.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verification&lt;/strong&gt;: &lt;code&gt;curl -I&lt;/code&gt; on IP and domain.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stick this on your desktop and spin up an &lt;strong&gt;oracle cloud free tier server&lt;/strong&gt; in minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;p&gt;Pick a path and keep the momentum going.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Easy:&lt;/strong&gt; Deploy a second static site on the same VM. Think of it like ordering a side dish at a restaurant—you already have the table, just add another plate. Create a new directory, drop an &lt;code&gt;index.html&lt;/code&gt; in there, and add a virtual host entry in &lt;code&gt;/etc/apache2/sites-available&lt;/code&gt;. Reload Apache and you’ll see two distinct sites sharing one IP.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Medium:&lt;/strong&gt; Install Docker Engine and spin up a containerized app. It’s similar to using Google Maps: the VM is the base map, Docker adds layers you can toggle on and off. Run&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;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; docker.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then pull your image with &lt;code&gt;docker run -d -p 8080:80 myimage&lt;/code&gt;. Your app now lives in an isolated sandbox.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hard:&lt;/strong&gt; Wire a CI/CD pipeline with GitHub Actions that pushes directly to OCI. Picture packing a suitcase: every item (code) gets placed in the right compartment (container) automatically. In your repo, add a &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt; that authenticates using an OCI token, builds the Docker image, and updates the instance. Once the workflow runs, new commits appear live without manual steps.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Keep an eye on the free‑tier limits; a stray large image can tip the budget.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tool:&lt;/strong&gt; Use &lt;code&gt;oci&lt;/code&gt; CLI for quick checks: &lt;code&gt;oci compute instance list&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VM IP → ssh -i mykey opc@&lt;em&gt;IP&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Docker start → &lt;code&gt;docker compose up -d&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Deploy action → &lt;code&gt;gh secret set OCI_TOKEN&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Got stuck or have a cool use case? Drop a comment below – I’d love to hear how you’re using Oracle’s free tier!&lt;/p&gt;







&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Abdullah Sheikh&lt;/a&gt;&lt;/strong&gt; is the Founder &amp;amp; CEO at &lt;a href="https://exteed.com/" rel="noopener noreferrer"&gt;Exteed&lt;/a&gt;, where he leads a team of skilled developers specializing in &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web2&lt;/a&gt; and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web3 applications&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Custom Smart Contracts&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain solutions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With 6+ years of experience, Abdullah has built &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;CRMs&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto Wallets&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;DeFi Exchanges&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;E-Commerce Stores&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;HIPAA Compliant EMR Systems&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;AI-powered systems&lt;/a&gt; that drive business efficiency and innovation.&lt;/p&gt;

&lt;p&gt;His expertise spans &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto &amp;amp; Tokenomics&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Artificial Intelligence&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web Applications&lt;/a&gt;; building reliable and smooth web apps that fit the client’s goals and requirements.&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:info@abdullah-sheikh.com"&gt;info@abdullah-sheikh.com&lt;/a&gt; · 🔗 &lt;a href="https://www.linkedin.com/in/-abdullah-sheikh/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · 🌐 &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;abdullah-sheikh.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>oraclecloud</category>
      <category>freetier</category>
      <category>selfhostedserver</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to Build a REST API with Node.js and Express from Scratch</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Tue, 19 May 2026 14:17:26 +0000</pubDate>
      <link>https://dev.to/-abdullah-sheikh/how-to-build-a-rest-api-with-nodejs-and-express-from-scratch-5d16</link>
      <guid>https://dev.to/-abdullah-sheikh/how-to-build-a-rest-api-with-nodejs-and-express-from-scratch-5d16</guid>
      <description>&lt;p&gt;&lt;em&gt;Create a production‑ready API step‑by‑step and deploy it in under an hour&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before We Start: What You'll Walk Away With
&lt;/h2&gt;

&lt;p&gt;By the end of this guide you’ll have a ready‑to‑run REST API that you built from the ground up.&lt;/p&gt;

&lt;p&gt;First you’ll set up a tidy project folder, run &lt;code&gt;npm init&lt;/code&gt;, add &lt;strong&gt;ESLint&lt;/strong&gt; for clean code, and drop a proper &lt;code&gt;.gitignore&lt;/code&gt; so nothing unwanted gets committed.&lt;/p&gt;

&lt;p&gt;Next you’ll write routes, middleware, validation, and error handling that follow the same rules you’d use when ordering food—a clear menu, a way to confirm the order, and a system to handle any mix‑ups.&lt;/p&gt;

&lt;p&gt;Finally you’ll run the API locally, wrap it in a Docker container, and push it to a free host, giving you a deployable service you can share with teammates or clients.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Scaffold the project with npm and configure ESLint.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement REST‑style endpoints, validation, and centralized error handling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Test locally, containerize with Docker, and deploy to a free platform.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Folder layout:&lt;/strong&gt; &lt;code&gt;src/&lt;/code&gt; for code, &lt;code&gt;test/&lt;/code&gt; for tests, &lt;code&gt;config/&lt;/code&gt; for env settings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Middleware chain:&lt;/strong&gt; request logger → validator → route handler → error catcher.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docker tip:&lt;/strong&gt; keep the image under 100 MB by using &lt;code&gt;node:alpine&lt;/code&gt; as the base.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This roadmap gives you a solid foundation to &lt;strong&gt;build REST API Node.js&lt;/strong&gt; projects without the usual guesswork.&lt;/p&gt;

&lt;p&gt;Ready to start the first step?&lt;/p&gt;

&lt;h2&gt;
  
  
  What a REST API Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;REST API&lt;/strong&gt; is simply a collection of URLs that other programs can call to read or change data. Each URL, called an endpoint, listens for standard HTTP verbs—&lt;code&gt;GET&lt;/code&gt; to fetch, &lt;code&gt;POST&lt;/code&gt; to create, &lt;code&gt;PUT&lt;/code&gt; to replace, and &lt;code&gt;DELETE&lt;/code&gt; to remove. The server always answers with a predictable format, typically JSON, so the caller knows exactly what to expect.&lt;/p&gt;

&lt;p&gt;Think of it like a restaurant menu. The menu lists dishes (endpoints) and tells you what ingredients you’ll get. When you order &lt;code&gt;GET /menu/pizza&lt;/code&gt;, the kitchen (server) returns a description of the pizza. If you want a custom pizza, you send &lt;code&gt;POST /order&lt;/code&gt; with your toppings, and the kitchen prepares it for you. No matter how many times you place the same order, you receive the same style of response.&lt;/p&gt;

&lt;p&gt;Because the menu never changes its layout, you can walk into any branch of the restaurant and order the same dish without confusion. That stability is what makes a REST API reliable for apps, mobile phones, or other services that need to share data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 4 Mistakes Everyone Makes With REST APIs
&lt;/h2&gt;

&lt;p&gt;Don’t let these four slip‑ups derail your first &lt;strong&gt;build REST API Node.js&lt;/strong&gt; project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mixing business logic into route handlers.&lt;/strong&gt; Think of a restaurant where the chef also takes orders, collects payment, and cleans tables. When the chef gets tangled in non‑cooking tasks, the kitchen slows down and you can’t easily test a single dish. In Express, putting database queries or calculations straight inside &lt;code&gt;app.get('/users')&lt;/code&gt; makes unit tests a nightmare.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ignoring proper HTTP status codes.&lt;/strong&gt; It’s like a GPS that always says “You have arrived” even when you’re still on the highway. Clients can’t tell if a request succeeded, failed, or needs retrying when you always return &lt;code&gt;200&lt;/code&gt; or default HTML pages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Skipping input validation.&lt;/strong&gt; Imagine packing a suitcase without checking the airline’s weight limit; you’ll end up paying extra fees or having items rejected. Without validating &lt;code&gt;req.body&lt;/code&gt; you expose your API to malformed data, SQL injection, and hard‑to‑track bugs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Forgetting a consistent error‑handling layer.&lt;/strong&gt; It’s like a storefront that shows a generic “Something went wrong” page instead of a clear error message. Without a centralized &lt;code&gt;errorHandler&lt;/code&gt; middleware, crashes bubble up as HTML, breaking the JSON contract your clients expect.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spot these early, and your API will stay clean, testable, and reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Build a REST API with Node.js and Express: Step‑By‑Step
&lt;/h2&gt;

&lt;p&gt;Run &lt;code&gt;npm init -y&lt;/code&gt; to create a default &lt;code&gt;package.json&lt;/code&gt;, then install the core tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;express dotenv joi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of this as ordering the basic ingredients before you start cooking.&lt;/p&gt;

&lt;p&gt;Lay out a tidy folder tree so you always know where to find things:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; src/&lt;span class="o"&gt;{&lt;/span&gt;routes,controllers,middleware,models&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s like packing a suitcase: each type of item gets its own compartment.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;src/app.js&lt;/code&gt;. Load environment variables, add &lt;code&gt;express.json()&lt;/code&gt; for body parsing, and attach the main router.&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="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;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&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;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;userRouter&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;./routes/userRoutes&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="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="nx"&gt;express&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="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/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;userRouter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define a router for a resource (e.g., users) in &lt;code&gt;src/routes/userRoutes.js&lt;/code&gt; using &lt;code&gt;express.Router()&lt;/code&gt;. Add the five CRUD 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;router&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="nc"&gt;Router&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;userController&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;../controllers/userController&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;validateUser&lt;/span&gt; &lt;span class="p"&gt;}&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;../middleware/validation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="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="nx"&gt;userController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAll&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="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;/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getOne&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;validateUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;validateUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move the actual work into &lt;code&gt;src/controllers/userController.js&lt;/code&gt;. For a quick start, use an in‑memory array named &lt;code&gt;users&lt;/code&gt;. Example for the fictional developer &lt;strong&gt;Alice&lt;/strong&gt;:&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;let&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="o"&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newUser&lt;/span&gt; &lt;span class="o"&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="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;body&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;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newUser&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;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAll&lt;/span&gt; &lt;span class="o"&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="nx"&gt;users&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;Add validation middleware in &lt;code&gt;src/middleware/validation.js&lt;/code&gt; with &lt;code&gt;joi&lt;/code&gt; to guard the request body before it reaches the controller.&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;Joi&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;joi&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;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&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="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&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="nf"&gt;required&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="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;email&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validateUser&lt;/span&gt; &lt;span class="o"&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="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&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="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;next&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;Create a global error‑handler in &lt;code&gt;src/middleware/errorHandler.js&lt;/code&gt; that catches thrown errors and sends a consistent JSON payload.&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="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attach it in &lt;code&gt;app.js&lt;/code&gt; after all routes: &lt;code&gt;app.use(require('./middleware/errorHandler'));&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Spin up the server with &lt;code&gt;src/server.js&lt;/code&gt;. Import the Express app and listen on &lt;code&gt;process.env.PORT&lt;/code&gt; (default 3000).&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;app&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;./app&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;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&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="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 &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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Write a few Jest + SuperTest specs in &lt;code&gt;tests/user.test.js&lt;/code&gt; to verify each endpoint returns the expected status and data.&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;request&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;supertest&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;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;../src/app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST /api/users creates a user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&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;request&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;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dockerize the project. A one‑line &lt;code&gt;Dockerfile&lt;/code&gt; copies the source, installs deps, and runs &lt;code&gt;node src/server.js&lt;/code&gt;. Pair it with a &lt;code&gt;docker-compose.yml&lt;/code&gt; that maps port 3000.&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; node:20-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&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="k"&gt;RUN &lt;/span&gt;npm ci &lt;span class="nt"&gt;--only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node","src/server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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'&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;api&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;.&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;3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A Real Example: Building a Todo List API for “Alex the Freelancer”
&lt;/h2&gt;

&lt;p&gt;Alex the freelancer wants a tiny API that lets him add, view, finish, and erase tasks—just like a personal notebook you can reach from any device.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the router file &lt;code&gt;src/routes/todos.js&lt;/code&gt; and register it in &lt;code&gt;src/app.js&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&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;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Router&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;todoController&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;../controllers/todoController&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="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="nx"&gt;todoController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAll&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;todoController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;todoController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;todoController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Write the controller that talks to the in‑memory store &lt;code&gt;src/models/todoStore.js&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt; &lt;span class="p"&gt;}&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;../models/todoStore&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;Joi&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;joi&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;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Joi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&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="nf"&gt;required&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAll&lt;/span&gt; &lt;span class="o"&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="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="o"&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="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newTodo&lt;/span&gt; &lt;span class="o"&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="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;completed&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="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTodo&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;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTodo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="o"&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="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&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="mi"&gt;404&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;Todo not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt; &lt;span class="o"&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="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&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="mi"&gt;404&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;Todo not found&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;end&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;ul&gt;
&lt;li&gt;Set up a global error handler in &lt;code&gt;src/app.js&lt;/code&gt; so missing IDs return a clean JSON message:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;500&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;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Internal Server Error&lt;/span&gt;&lt;span class="dl"&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;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&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;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Validation:&lt;/strong&gt; &lt;code&gt;joi&lt;/code&gt; guarantees every new task has a non‑empty &lt;code&gt;title&lt;/code&gt;, just like a restaurant checks your order isn’t blank.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Testing:&lt;/strong&gt; Run &lt;code&gt;npm test&lt;/code&gt;. The suite posts a todo then fetches it, confirming the flow works.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docker:&lt;/strong&gt; Build with &lt;code&gt;docker build -t alex/todo-api .&lt;/code&gt; and launch via &lt;code&gt;docker compose up&lt;/code&gt;. Alex now sees his API live on &lt;code&gt;localhost:3000&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these pieces in place, Alex can finally focus on his projects instead of wrestling with a broken backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tools That Make This Easier
&lt;/h2&gt;

&lt;p&gt;Grab the right gear before you start building your REST API with Node.js.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;VS Code + ESLint &amp;amp; Prettier&lt;/strong&gt; – Think of ESLint as a spell‑checker for your code and Prettier as the auto‑formatter that keeps everything neat, just like a kitchen drawer organized by size.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Postman (free tier)&lt;/strong&gt; – It’s the “menu” you use to order a request, see the response, and then print the receipt as documentation. Perfect for sanity‑checking each endpoint.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docker Desktop&lt;/strong&gt; – Packs your API into a portable container the way a suitcase bundles all your travel gear, so it runs the same on any machine.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Jest + SuperTest&lt;/strong&gt; – Jest runs the test suite, while SuperTest acts like a delivery driver that quickly knocks on each route to confirm it’s open.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Render.com (free hobby plan)&lt;/strong&gt; – Deploys your service with a single click, similar to dropping a finished dish onto a table for guests to enjoy.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these tools in place, the rest of the process feels less like a guess‑work project and more like following a well‑marked trail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: REST API with Node.js Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Grab this cheat sheet and keep it beside your editor; it captures every step you need to &lt;strong&gt;build REST API Node.js&lt;/strong&gt; without hunting through paragraphs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Initialize project&lt;/strong&gt; – run &lt;code&gt;npm init -y&lt;/code&gt;, then &lt;code&gt;npm install express dotenv joi jest supertest&lt;/code&gt;. Think of it as ordering the basic ingredients before cooking.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Folder layout&lt;/strong&gt; – create &lt;code&gt;src/{routes,controllers,middleware,models}&lt;/code&gt;. It’s like packing a suitcase: each compartment holds a specific type of item.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;app.js&lt;/strong&gt; – set up &lt;code&gt;express.json()&lt;/code&gt;, mount the main router, and attach a centralized error handler. This file is the entry gate, similar to a lobby directing visitors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Routes&lt;/strong&gt; – define GET, POST, PUT, DELETE with &lt;code&gt;express.Router()&lt;/code&gt;. Each route is a street sign pointing to a destination.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Controllers&lt;/strong&gt; – pure functions that handle the request and response; keep DB calls out of the router. For example, &lt;em&gt;Alex&lt;/em&gt; the developer writes:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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;getUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&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;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findById&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;params&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&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;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;User not found&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;user&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;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Validation&lt;/strong&gt; – craft a &lt;code&gt;joi&lt;/code&gt; schema and plug it into a middleware function. It works like a bouncer checking IDs before letting anyone in.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Error handling&lt;/strong&gt; – one middleware catches all errors and returns &lt;code&gt;{ error, message }&lt;/code&gt;. Centralized handling is the fire alarm that alerts everyone at once.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tests&lt;/strong&gt; – write &lt;code&gt;jest&lt;/code&gt; suites with &lt;code&gt;supertest&lt;/code&gt; for each route. Think of them as quality checks before shipping a product.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dockerfile&lt;/strong&gt; – use &lt;code&gt;FROM node:20-alpine&lt;/code&gt;, copy &lt;code&gt;src&lt;/code&gt;, run &lt;code&gt;npm ci&lt;/code&gt;, then &lt;code&gt;CMD ["node","src/server.js"]&lt;/code&gt;. It’s the sealed container you’d load onto a truck.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploy&lt;/strong&gt; – push the Docker image to Render (or similar), set the &lt;code&gt;PORT&lt;/code&gt; env variable, and your API goes live.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep this list open; you’ll reference it each time you spin up a new service.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Do Next
&lt;/h2&gt;

&lt;p&gt;Pick one of these upgrades and start expanding your API right away.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add a real database&lt;/strong&gt; – swap the in‑memory array for PostgreSQL using Prisma. Think of it like moving from a kitchen counter pantry to a full‑size fridge: you can store more, keep things fresh, and retrieve exactly what you need later.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Install &lt;code&gt;prisma&lt;/code&gt; and run &lt;code&gt;npx prisma init&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Define a &lt;code&gt;Todo&lt;/code&gt; model in &lt;code&gt;schema.prisma&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Generate the client and replace your current CRUD functions with &lt;code&gt;prisma.todo.findMany()&lt;/code&gt;, &lt;code&gt;prisma.todo.create()&lt;/code&gt;, etc.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Implement JWT authentication&lt;/strong&gt; so only registered users can manage their todos. It's like giving each diner a badge that lets them order from the kitchen; without the badge, the kitchen stays closed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Install &lt;code&gt;jsonwebtoken&lt;/code&gt; and &lt;code&gt;bcrypt&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create &lt;code&gt;/register&lt;/code&gt; and &lt;code&gt;/login&lt;/code&gt; routes that hash passwords and issue a token.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add a middleware that verifies the token on protected routes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Write OpenAPI (Swagger) documentation&lt;/strong&gt; and generate client SDKs with Swagger Codegen. Imagine handing a Google Maps sheet to a delivery driver; the map tells them every turn without guessing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create &lt;code&gt;swagger.yaml&lt;/code&gt; describing your endpoints.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Serve it with &lt;code&gt;swagger-ui-express&lt;/code&gt; at &lt;code&gt;/api-docs&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;swagger-codegen generate -i swagger.yaml -l javascript -o ./client-sdk&lt;/code&gt; to get a ready‑to‑use client.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which of these steps are you tackling next? Let me know in the comments!&lt;/p&gt;







&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Abdullah Sheikh&lt;/a&gt;&lt;/strong&gt; is the Founder &amp;amp; CEO at &lt;a href="https://exteed.com/" rel="noopener noreferrer"&gt;Exteed&lt;/a&gt;, where he leads a team of skilled developers specializing in &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web2&lt;/a&gt; and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web3 applications&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Custom Smart Contracts&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain solutions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With 6+ years of experience, Abdullah has built &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;CRMs&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto Wallets&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;DeFi Exchanges&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;E-Commerce Stores&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;HIPAA Compliant EMR Systems&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;AI-powered systems&lt;/a&gt; that drive business efficiency and innovation.&lt;/p&gt;

&lt;p&gt;His expertise spans &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Blockchain&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Crypto &amp;amp; Tokenomics&lt;/a&gt;, &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Artificial Intelligence&lt;/a&gt;, and &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;Web Applications&lt;/a&gt;; building reliable and smooth web apps that fit the client’s goals and requirements.&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:info@abdullah-sheikh.com"&gt;info@abdullah-sheikh.com&lt;/a&gt; · 🔗 &lt;a href="https://www.linkedin.com/in/-abdullah-sheikh/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; · 🌐 &lt;a href="https://abdullah-sheikh.com/" rel="noopener noreferrer"&gt;abdullah-sheikh.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>express</category>
      <category>restapi</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
