<?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: Abdullah Sheikh</title>
    <description>The latest articles on Forem by Abdullah Sheikh (@-abdullah-sheikh).</description>
    <link>https://forem.com/-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>Forem: Abdullah Sheikh</title>
      <link>https://forem.com/-abdullah-sheikh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/-abdullah-sheikh"/>
    <language>en</language>
    <item>
      <title>How to Build a Full‑Stack App with Next.js and PostgreSQL in 8 Simple Steps</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Sat, 23 May 2026 12:07:31 +0000</pubDate>
      <link>https://forem.com/-abdullah-sheikh/how-to-build-a-full-stack-app-with-nextjs-and-postgresql-in-8-simple-steps-361c</link>
      <guid>https://forem.com/-abdullah-sheikh/how-to-build-a-full-stack-app-with-nextjs-and-postgresql-in-8-simple-steps-361c</guid>
      <description>&lt;p&gt;&lt;em&gt;Create a production‑ready Next.js front‑end, connect it to a PostgreSQL database, and deploy with zero‑config hosting&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 working full‑stack app that you can point a browser at and see real data being saved.&lt;/p&gt;

&lt;p&gt;First you’ll scaffold a &lt;strong&gt;Next.js&lt;/strong&gt; project with TypeScript and wire it up to a PostgreSQL database using &lt;strong&gt;Prisma&lt;/strong&gt;. Think of it like setting up a kitchen: you install the stove (Next.js), plug in the gas line (PostgreSQL), and lay out the recipe book (Prisma schema).&lt;/p&gt;

&lt;p&gt;Next you’ll create a simple CRUD API route and a React UI that talks to it, so you can add, read, update, and delete records that live in the database. It’s similar to ordering a coffee: the UI is you placing the order, the API is the barista, and the database is the coffee beans you’re actually consuming.&lt;/p&gt;

&lt;p&gt;Finally you’ll push the whole stack to a cloud host (Vercel or Railway) with a single command and get a live URL. Deploying is like packing a suitcase: once everything’s folded neatly, you zip it up and head out the door.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;npx create-next-app@latest --ts&lt;/code&gt; and add &lt;code&gt;prisma&lt;/code&gt; as a dev dependency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure the &lt;code&gt;DATABASE_URL&lt;/code&gt; in &lt;code&gt;.env&lt;/code&gt; and run &lt;code&gt;npx prisma init&lt;/code&gt; to create the schema.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write API handlers in &lt;code&gt;/pages/api&lt;/code&gt; that use Prisma client methods.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build React components that call &lt;code&gt;/api&lt;/code&gt; endpoints with &lt;code&gt;fetch&lt;/code&gt; or &lt;code&gt;axios&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploy with &lt;code&gt;vercel&lt;/code&gt; (or &lt;code&gt;railway up&lt;/code&gt;) and watch your app go live.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Toolchain:&lt;/strong&gt; Next.js, TypeScript, Prisma, PostgreSQL, Vercel/Railway.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt; &lt;code&gt;npx prisma migrate dev&lt;/code&gt; to sync schema, &lt;code&gt;npm run dev&lt;/code&gt; to test locally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Keep &lt;code&gt;.env&lt;/code&gt; out of version control; use Vercel’s dashboard to set env vars.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you’re ready to dive into the first step.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Full‑Stack Next.js App Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;A full‑stack Next.js app is simply a React UI that also runs server‑side code, all from one project.&lt;/p&gt;

&lt;p&gt;The UI lives in &lt;strong&gt;pages&lt;/strong&gt; or &lt;strong&gt;app&lt;/strong&gt; directories and gets rendered on the server, so the first page load is fast and SEO‑friendly. Beside those pages you can create &lt;strong&gt;API routes&lt;/strong&gt;—tiny Node.js functions that respond to &lt;code&gt;/api/*&lt;/code&gt; requests. Those routes talk to your database, perform business logic, and send JSON back to the front‑end.&lt;/p&gt;

&lt;p&gt;Imagine a restaurant. The dining room is the React front‑of‑house where guests see menus and plates. The kitchen is the API layer, where chefs pull ingredients from the pantry (your PostgreSQL database), cook them, and hand the finished dish back to the server. Both rooms share the same building, so the manager (your Next.js project) can coordinate staffing, opening hours, and deliveries in one place.&lt;/p&gt;

&lt;p&gt;Because everything lives under one roof, you ship one repo, run one &lt;code&gt;npm run dev&lt;/code&gt;, and scale the whole thing with a single Vercel or Railway deployment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Server‑rendered pages&lt;/strong&gt;: Fast first paint, good for SEO.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;API routes&lt;/strong&gt;: Tiny back‑end functions right next to your UI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PostgreSQL&lt;/strong&gt;: The pantry that stores all your structured data.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s the essence of a Next.js PostgreSQL tutorial: a single codebase that serves both the “front‑of‑house” and the “kitchen.”&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5 Mistakes Everyone Makes With Next.js + PostgreSQL
&lt;/h2&gt;

&lt;p&gt;Here’s what trips most developers up when they pair Next.js with PostgreSQL.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mixing client‑side fetches with server‑only env vars.&lt;/strong&gt; Think of env vars as a secret sauce kept in the kitchen; exposing them in a fetch call is like sprinkling that sauce on a table for everyone to see. Use &lt;code&gt;getServerSideProps&lt;/code&gt; or API routes to keep &lt;code&gt;process.env.DATABASE_URL&lt;/code&gt; strictly on the server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using raw SQL strings instead of an ORM.&lt;/strong&gt; Writing raw queries is like ordering every ingredient separately at a restaurant – you’ll lose track of what belongs together. Prisma gives you a typed, auto‑complete API that keeps your queries consistent and easier to refactor.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Forgetting to run Prisma migrations on the remote DB.&lt;/strong&gt; Deploying without migrating is like moving into a new house and leaving the doors locked; the app can’t get in. Run &lt;code&gt;npx prisma migrate deploy&lt;/code&gt; as part of your CI/CD pipeline to keep production in sync.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Placing API logic inside page components.&lt;/strong&gt; Embedding data‑fetching code in &lt;code&gt;pages/index.js&lt;/code&gt; is like packing a suitcase inside another suitcase – it gets messy fast. Move the logic to &lt;code&gt;/pages/api&lt;/code&gt; or a dedicated &lt;code&gt;/lib&lt;/code&gt; folder for clarity and reuse.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploying to a host that doesn’t support Postgres connections out‑of‑the‑box.&lt;/strong&gt; Imagine booking a hotel that doesn’t have Wi‑Fi; you’ll be stuck. &lt;em&gt;Case in point:&lt;/em&gt; &lt;strong&gt;Alex&lt;/strong&gt;, a freelance dev, pushed a Next.js app to a generic static‑site host, only to see “connection timeout” errors because the platform never opened a TCP port for PostgreSQL. Choose Vercel, Render, or Fly.io, which provide native Postgres support.&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;Keep &lt;code&gt;process.env.*&lt;/code&gt; in server‑only code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prefer Prisma over raw SQL.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;prisma migrate deploy&lt;/code&gt; on every deploy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Separate API routes from page components.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pick a host with built‑in Postgres connectivity.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Build a Full‑Stack App with Next.js and PostgreSQL: Step‑by‑Step
&lt;/h2&gt;

&lt;p&gt;Grab a terminal and fire up a fresh Next.js project with TypeScript.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;npx create-next-app@latest my-app --ts&lt;/code&gt;. It’s like ordering a starter kit straight from the menu.&lt;/p&gt;

&lt;p&gt;Install Prisma and add your database URL.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;npm install prisma @prisma/client&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create &lt;code&gt;.env.local&lt;/code&gt; and set &lt;code&gt;DATABASE_URL=postgresql://USER:PASS@HOST:5432/dbname&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Initialize Prisma and define the data model.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Edit &lt;code&gt;prisma/schema.prisma&lt;/code&gt; to add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model Task {
  id        Int      @id @default(autoincrement())
  title     String
  completed Boolean  @default(false)
  createdAt DateTime @default(now())
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then migrate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx prisma migrate dev &lt;span class="nt"&gt;--name&lt;/span&gt; init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate the client so your code can talk to the DB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx prisma generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create API routes under &lt;code&gt;pages/api/tasks&lt;/code&gt; for CRUD.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;GET /api/tasks&lt;/code&gt; – fetch all tasks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;POST /api/tasks&lt;/code&gt; – create a new task&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;PUT /api/tasks/[id]&lt;/code&gt; – update status&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;DELETE /api/tasks/[id]&lt;/code&gt; – remove a task&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each handler imports the generated Prisma client and runs the appropriate query.&lt;/p&gt;

&lt;p&gt;Build the UI in &lt;code&gt;components/TaskList.tsx&lt;/code&gt;. Use &lt;code&gt;fetch&lt;/code&gt; or &lt;code&gt;SWR&lt;/code&gt; to call the API, just like a map app polling for live traffic.&lt;/p&gt;

&lt;p&gt;Optional: add a tiny middleware that checks a static &lt;code&gt;API_TOKEN&lt;/code&gt; header. It’s the same as asking for a password before entering a private room.&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;tokenGuard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &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;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;API_TOKEN&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;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;401&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;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the dev server and make sure everything works.&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;Open &lt;code&gt;http://localhost:3000&lt;/code&gt; and add, toggle, or delete tasks.&lt;/p&gt;

&lt;p&gt;Push the repository to GitHub.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git init
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit"&lt;/span&gt;
git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
git remote add origin https://github.com/yourname/my-app.git
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connect the repo to Vercel (or Railway) and paste the same &lt;code&gt;DATABASE_URL&lt;/code&gt; into the platform’s env settings. The next build will spin up your Next.js front‑end and a Postgres instance that talk automatically.&lt;/p&gt;

&lt;p&gt;Now you have a working full‑stack app ready to ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example: Building a Team Todo List for Startup Founder Maya
&lt;/h2&gt;

&lt;p&gt;Maya’s co‑founders need a place to drop tasks the way you’d toss a quick note onto a kitchen fridge.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define the Prisma model. In &lt;code&gt;prisma/schema.prisma&lt;/code&gt; add:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model Task {
  id        Int      @id @default(autoincrement())
  title     String
  completed Boolean  @default(false)
  createdAt DateTime @default(now())
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create the API routes. Each file lives under &lt;code&gt;pages/api/tasks&lt;/code&gt; and mirrors a restaurant’s order‑taking system:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;GET /api/tasks&lt;/code&gt; – fetch the menu (all tasks).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;POST /api/tasks&lt;/code&gt; – place a new order (add a task).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;PUT /api/tasks/[id]&lt;/code&gt; – update an existing dish (toggle completion or edit title).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;DELETE /api/tasks/[id]&lt;/code&gt; – remove a dish you don’t want.&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;prisma&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;../../../lib/prisma&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &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;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tasks&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;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&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;tasks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// POST, PUT, DELETE omitted for brevity&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;Wire the UI. In &lt;code&gt;components/TaskList.js&lt;/code&gt; render a list, a form, and check‑boxes. Think of it as a whiteboard where Maya can scribble and erase in real time.
&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;TaskList&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;e&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&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;/api/tasks&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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&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;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&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="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;})})&lt;/span&gt;
    &lt;span class="nf"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// toggle and delete handlers similar&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;strong&gt;Tool tip:&lt;/strong&gt; Use &lt;code&gt;useSWR&lt;/code&gt; for instant UI refresh after each API call.&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;&lt;p&gt;&lt;strong&gt;Model:&lt;/strong&gt; &lt;code&gt;Task&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Routes:&lt;/strong&gt; GET, POST, PUT, DELETE under &lt;code&gt;/api/tasks&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploy:&lt;/strong&gt; Push to GitHub → Vercel → add &lt;code&gt;DATABASE_URL&lt;/code&gt; from Railway → Deploy&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After Maya pushes the repo, links it to Vercel, drops the Railway &lt;code&gt;DATABASE_URL&lt;/code&gt; into the env panel, and clicks Deploy, she gets a shareable URL in under two minutes. Now her team can add, edit, and delete tasks as easily as ordering coffee.&lt;/p&gt;

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

&lt;p&gt;Grab the right helpers and the whole stack feels like ordering a single meal instead of juggling separate dishes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prisma 2&lt;/strong&gt; – Think of it as a smart waiter who already knows the menu. It reads your &lt;code&gt;schema.prisma&lt;/code&gt;, serves a type‑safe client, and translates your JavaScript calls into perfect PostgreSQL queries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Vercel&lt;/strong&gt; – This is the drive‑through lane for Next.js. Push to Git and Vercel spins up a serverless instance, handling builds and CDN caching without you touching a config file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Railway&lt;/strong&gt; – Imagine a grocery store that gives you a bag of pre‑packed ingredients and a single barcode. Railway’s free tier drops a managed PostgreSQL instance with a one‑click connection string you can paste into &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SWR&lt;/strong&gt; – Like Google Maps constantly refreshing traffic, SWR fetches, caches, and revalidates data for you, so your UI never stalls waiting for the server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;GitHub Codespaces&lt;/strong&gt; – Picture a fully stocked kitchen that appears wherever you are. Launch a Codespace, run &lt;code&gt;npx prisma migrate dev&lt;/code&gt;, preview the app on port 3000, all without installing anything locally.&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;&lt;p&gt;&lt;code&gt;npm i @prisma/client prisma&lt;/code&gt; – install Prisma.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;npx prisma init&lt;/code&gt; – creates &lt;code&gt;schema.prisma&lt;/code&gt; and .env.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploy with &lt;code&gt;vercel --prod&lt;/code&gt; after pushing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy Railway’s URL into &lt;code&gt;DATABASE_URL&lt;/code&gt; in Vercel’s env.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;code&gt;useSWR('/api/posts')&lt;/code&gt; in React components.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these five tools the heavy lifting disappears, letting you focus on the product instead of the plumbing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: Full‑Stack Next.js + PostgreSQL Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Grab this cheat sheet whenever you need a quick reminder while building your Next.js PostgreSQL tutorial app.&lt;/p&gt;

&lt;p&gt;🔹 Scaffold with TypeScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-next-app@latest my-app &lt;span class="nt"&gt;--ts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;– think of it like ordering a pizza with a ready‑made crust.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔹 Store the connection string: create &lt;code&gt;.env.local&lt;/code&gt; and add &lt;code&gt;DATABASE_URL=postgresql://…&lt;/code&gt; – it’s the address you’d give a delivery driver.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔹 Define your data model in &lt;code&gt;prisma/schema.prisma&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;model Task {
  id        Int      @id @default(autoincrement())
  title     String
  completed Boolean  @default(false)
  createdAt DateTime @default(now())
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;– like packing a suitcase with exactly the items you need.&lt;br&gt;
🔹 Apply the schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx prisma migrate dev &lt;span class="nt"&gt;--name&lt;/span&gt; init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;– similar to syncing a new map on your phone before a road trip.&lt;br&gt;
🔹 Create an API route. Example for &lt;code&gt;pages/api/tasks.ts&lt;/code&gt; used by &lt;em&gt;Alice&lt;/em&gt; the project manager:&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;PrismaClient&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;@prisma/client&lt;/span&gt;&lt;span class="dl"&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="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrismaClient&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tasks&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;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&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;tasks&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &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;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;title&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;task&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;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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="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;task&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Allow&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&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;POST&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="mi"&gt;405&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="s2"&gt;`Method &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;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; not allowed`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔹 Fetch data on the client with SWR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useSWR&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;swr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSWR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/tasks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetcher&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;– like checking a live traffic map for the fastest route.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔹 Deploy in two clicks: push &lt;code&gt;my-app&lt;/code&gt; to GitHub, connect the repo to Vercel, add the same &lt;code&gt;DATABASE_URL&lt;/code&gt; on Railway, and Vercel gives you a live URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔹 Watch out for common pitfalls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Never expose &lt;code&gt;.env.local&lt;/code&gt; – it’s like leaving your house key under the doormat.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Always run migrations on the server – skipping this is like skipping a safety check before a flight.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoid raw SQL strings in your code – they’re harder to read than a handwritten recipe.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep this list handy and you’ll move from prototype to production without missing a beat.&lt;/p&gt;

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

&lt;p&gt;Pick a next step, get your app feeling more like a real product, and keep the momentum going.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Add authentication&lt;/strong&gt; – plug in &lt;a href="https://next-auth.js.org/" rel="noopener noreferrer"&gt;NextAuth.js&lt;/a&gt; so Maya’s team can sign in securely.&lt;/p&gt;

&lt;p&gt;Think of it like handing out key cards at the office: only the right people get in, and you don’t have to build the lock yourself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;NextAuth&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next-auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;GithubProvider&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next-auth/providers/github&lt;/span&gt;&lt;span class="dl"&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="nc"&gt;NextAuth&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;GithubProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_SECRET&lt;/span&gt; &lt;span class="p"&gt;})],&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DATABASE_URL&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;Implement optimistic UI updates&lt;/strong&gt; with SWR’s &lt;code&gt;mutate&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;It’s like ordering a coffee and sipping it before the barista finishes – the UI feels instant, and you roll back if the server says “nope.”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useSWR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mutate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;swr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSWR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/tasks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// after posting a new task&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="s2"&gt;/api/tasks&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;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nf"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/tasks&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;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newTask&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Containerize and scale&lt;/strong&gt; – write a &lt;code&gt;Dockerfile&lt;/code&gt;, build an image, then drop it into a Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;Imagine packing your suitcase (the app) into a sealed box (Docker) and loading it onto a cargo ship (K8s) that can carry thousands of identical boxes.&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:18-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;--production&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm run build
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 3000&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;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Keep your &lt;code&gt;.env&lt;/code&gt; files out of the image; use Kubernetes Secrets for DB credentials.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Enable health checks in your deployment YAML so the cluster knows when a pod is ready.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💬 Got stuck or have a cooler feature idea? Drop a comment below and let’s chat!&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>nextjs</category>
      <category>postgres</category>
      <category>fullstack</category>
      <category>prisma</category>
    </item>
    <item>
      <title>How to Use n8n to Automate Repetitive Work Tasks in Minutes</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Fri, 22 May 2026 12:06:03 +0000</pubDate>
      <link>https://forem.com/-abdullah-sheikh/how-to-use-n8n-to-automate-repetitive-work-tasks-in-minutes-1m0</link>
      <guid>https://forem.com/-abdullah-sheikh/how-to-use-n8n-to-automate-repetitive-work-tasks-in-minutes-1m0</guid>
      <description>&lt;p&gt;&lt;em&gt;Learn step‑by‑step how to set up n8n workflows that eliminate manual chores and boost productivity&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 able to set up a real‑world automation without touching any code.&lt;/p&gt;

&lt;p&gt;First you’ll grasp the three building blocks of &lt;strong&gt;n8n&lt;/strong&gt;—triggers, nodes, and variables—just like learning the ingredients, recipe, and seasoning for a quick meal.&lt;/p&gt;

&lt;p&gt;Next you’ll assemble a workflow that pulls new Gmail messages, drops the relevant fields into a Google Sheet, and pings a Slack channel, mirroring how a GPS routes you from point A to B while you focus on the drive.&lt;/p&gt;

&lt;p&gt;Finally you’ll walk away with a cheat‑sheet of copy‑paste snippets and a list of go‑to community resources, ready to reuse whenever a new repetitive task pops up.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Identify the event that starts the automation (trigger).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose the actions that move and transform data (nodes).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pass information between steps with variables (like packing a suitcase with exactly what you need).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trigger snippets&lt;/strong&gt;: Gmail “New Email” → &lt;code&gt;trigger&lt;/code&gt; node example.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Node snippets&lt;/strong&gt;: Google Sheets “Append Row” → &lt;code&gt;googleSheets&lt;/code&gt; node example.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Variable snippets&lt;/strong&gt;: &lt;code&gt;{{$json["subject"]}}&lt;/code&gt; to reference email subject.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Community hub&lt;/strong&gt;: n8n.io/forum – searchable threads and ready‑made workflows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Template library&lt;/strong&gt;: n8n.io/workflows – filter by “Gmail”, “Sheets”, “Slack”.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docs shortcut&lt;/strong&gt;: docs.n8n.io – quick reference for node options.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Grab these tools, follow the steps, and you’ll turn a handful of minutes into a reliable, repeatable automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What n8n Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;n8n&lt;/strong&gt; is an open‑source, node‑based automation platform that lets you link your favorite apps without writing a single line of code. You drag a block, set a few fields, and the tool handles the rest.&lt;/p&gt;

&lt;p&gt;Picture a programmable LEGO set. Each brick—called a &lt;strong&gt;node&lt;/strong&gt;—does one specific job, like pulling emails from Gmail or posting a message to Slack. Snap the bricks together in the order you need, and you’ve built a tiny machine that runs automatically.&lt;/p&gt;

&lt;p&gt;Because it’s open source, you can host n8n on your own server, keep your data private, and add community‑made nodes whenever a new service appears. That freedom means you’re not locked into a pricey SaaS plan.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Triggers&lt;/strong&gt;: the first node that wakes the workflow, like a new row in a Google Sheet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Actions&lt;/strong&gt;: what happens next, such as sending a notification or updating a CRM.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flows&lt;/strong&gt;: the sequence of nodes that turn a simple trigger into a full‑fledged process.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it like ordering a custom pizza. You choose the crust (trigger), add toppings (actions), and the kitchen (n8n) assembles everything exactly how you specified, delivering the finished slice without you having to do the chopping.&lt;/p&gt;

&lt;p&gt;That’s the core of the &lt;em&gt;n8n automation guide&lt;/em&gt;: a toolbox you can assemble piece by piece to ditch the copy‑paste grind.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 Mistakes Everyone Makes With n8n
&lt;/h2&gt;

&lt;p&gt;Most people trip over the same three things when they first tinker with n8n.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Over‑complicating the flow&lt;/strong&gt; – You start adding nodes like you’d pile extra toppings on a pizza, even when a single “Set” node could handle the job. Those built‑in shortcuts exist to keep the workflow lean; skip the garnish and the recipe stays tasty.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ignoring error handling&lt;/strong&gt; – It’s like driving without a spare tire; you’ll keep going until a flat stops you cold. Without a &lt;code&gt;Catch&lt;/code&gt; node, a failed API call leaves the whole automation silent, and you never know why the process stalled.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Not using environment variables&lt;/strong&gt; – Hard‑coding passwords is like writing your house key on a sticky note and taping it to the front door. Store credentials in &lt;code&gt;ENV&lt;/code&gt; variables and reference them; swapping a key later won’t require reopening every node.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quick tip:&lt;/strong&gt; Use the “Merge” node’s “Keep Only Set” option to replace long chains of &lt;code&gt;IF&lt;/code&gt; nodes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quick tip:&lt;/strong&gt; Add a final &lt;code&gt;Catch&lt;/code&gt; node that sends a Slack alert with &lt;code&gt;{{ $json["error"] }}&lt;/code&gt; so you’re never left guessing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quick tip:&lt;/strong&gt; Define &lt;code&gt;API_KEY&lt;/code&gt; in the n8n settings panel and call it via &lt;code&gt;{{$env.API_KEY}}&lt;/code&gt; in any credential field.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spotting these pitfalls early saves you hours of debugging and keeps your n8n automation guide experience smooth.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Automate Repetitive Work Tasks with n8n: Step‑by‑Step
&lt;/h2&gt;

&lt;p&gt;Let’s walk through the exact clicks you need to turn a Gmail inbox into a live Slack alert and a Google Sheet log.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sign up or spin up n8n.&lt;/strong&gt; Grab a free account on &lt;code&gt;n8n.cloud&lt;/code&gt; or run the 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;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; n8nio/n8n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. Think of it like ordering a coffee: you either pick up a ready‑made cup or brew it yourself.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a new workflow and pick a trigger.&lt;/strong&gt; Click “New Workflow”, then add the &lt;em&gt;Gmail → New Email&lt;/em&gt; trigger. It’s the “start button” that watches your inbox the way a GPS watches your location.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add the Google Sheets node.&lt;/strong&gt; Drag “Google Sheets → Append Row” onto the canvas. Map &lt;code&gt;subject&lt;/code&gt;, &lt;code&gt;from&lt;/code&gt;, and &lt;code&gt;date&lt;/code&gt; to the columns you set up. This is like packing a suitcase: you decide which item goes into which compartment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Insert a Slack notification.&lt;/strong&gt; Add “Slack → Send Message”, select the channel, and pull the same email fields into the message text. Now every new mail pops up in Slack the way a doorbell rings when someone arrives.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set up error handling.&lt;/strong&gt; Drop a “Catch” node, connect it to a second “Slack → Send Message” that posts to a #n8n‑errors channel. If anything goes wrong, you’ll be alerted instantly instead of discovering the issue later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test, tweak, activate.&lt;/strong&gt; Send yourself a test email, run the workflow, check the sheet and Slack. Adjust field mappings if needed, then flip the “Active” switch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Keep the workflow name descriptive, like &lt;code&gt;Inbox‑to‑Sheet‑Slack&lt;/code&gt;, so you can find it later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat sheet:&lt;/strong&gt; Trigger → Gmail New Email | Action 1 → Google Sheets Append Row | Action 2 → Slack Send Message | Error → Catch → Slack Error Channel&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it—your repetitive email handling is now fully automated.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example: Automating Daily Lead Capture for a Sales Team
&lt;/h2&gt;

&lt;p&gt;Maya gets a flood of lead emails every morning, tags them “New Lead”, and wishes they could jump straight into the team’s spreadsheet and Slack channel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trigger: Gmail – New Email with Label&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set the node to watch the label &lt;code&gt;New Lead&lt;/code&gt;. In the screenshot you’d see the label field filled, and the “Only with attachment” toggle off – we just need the body text.&lt;br&gt;
&lt;strong&gt;Node: Google Sheets – Append Row&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Choose the spreadsheet &lt;code&gt;Leads 2025&lt;/code&gt; and the sheet tab “Raw”. Map these fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;{{$json["from"]}}&lt;/code&gt; → “Email” column&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;{{$json["subject"]}}&lt;/code&gt; → “Subject” column&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;{{$json["text"]}}&lt;/code&gt; → “Notes” column&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The screenshot shows the green “Map” button with these three variables highlighted.&lt;br&gt;
&lt;strong&gt;Node: Slack – Send Message&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Point it at the channel &lt;code&gt;#sales-leads&lt;/code&gt;. In the message box type: &lt;code&gt;New lead from {{$json["from"]}} – check the sheet!&lt;/code&gt;. The preview in the screenshot displays the rendered text with Maya’s actual sender address.&lt;br&gt;
&lt;strong&gt;Connect &amp;amp; Test&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Drag arrows so the Gmail trigger feeds into Sheets, then into Slack. Click “Execute Workflow” – the test email appears as a new row and a Slack ping pops up. If anything looks off, the node logs (shown in the bottom pane) tell you exactly which field missed the mapping.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Keep the Gmail label tidy; a single typo prevents the trigger from firing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use the “Auto‑Map” feature in Sheets if your column names match the JSON keys.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat Sheet:&lt;/strong&gt; &lt;code&gt;Gmail → Sheets → Slack&lt;/code&gt; is the fastest three‑step loop for daily lead capture.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s the whole n8n automation guide in action for Maya’s lead workflow.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Tools That Make This Easier
&lt;/h2&gt;

&lt;p&gt;Pick the right helpers and you’ll spin up a workflow faster than ordering a coffee.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;n8n.cloud&lt;/strong&gt; – The hosted SaaS version. It’s the “Google Maps” of automation: you type a destination, it routes you without worrying about servers. The free tier gives 2,000 executions each month, enough for most small‑team chores.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docker Desktop&lt;/strong&gt; – If you prefer to keep everything on your own laptop, Docker lets you launch a self‑hosted n8n instance with a single command, like packing a suitcase: you decide what to bring and it’s ready when you arrive.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;n8n.io Community Forum&lt;/strong&gt; – Think of it as a public library of workflow snippets. Search “CSV to Google Sheet” and paste the ready‑made JSON straight into your editor.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Postman&lt;/strong&gt; – Before you drop an API call into an n8n node, test it here. It’s the “try‑before‑you‑buy” step that saves you from cryptic errors later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zapier Alternative Comparison&lt;/strong&gt; – Zapier excels at one‑click integrations, but when you need custom data mapping, n8n lets you rearrange fields like you’d rearrange furniture in a room. The result is a tighter fit for unique business logic.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these tools in your kit, the &lt;em&gt;n8n automation guide&lt;/em&gt; becomes a practical toolbox rather than a theory lecture.&lt;/p&gt;
&lt;h2&gt;
  
  
  Quick Reference: n8n Automation Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Grab this cheat sheet, paste it next to your coffee, and you’ll have n8n automation guide at a glance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core concepts&lt;/strong&gt; – Think of a &lt;em&gt;Trigger&lt;/em&gt; as the doorbell that starts the workflow, a &lt;em&gt;Node&lt;/em&gt; as the kitchen station where you prep the dish, &lt;em&gt;Variable&lt;/em&gt; as the ingredient you pass along, and &lt;em&gt;Error handling&lt;/em&gt; as the fire extinguisher you keep handy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3 common pitfalls to avoid&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Hard‑coding IDs – they break when you rename a sheet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Skipping &lt;code&gt;Set&lt;/code&gt; nodes – you lose track of data shape.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ignoring retry settings – a single API hiccup can stall the whole chain.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;6‑step workflow creation checklist&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Pick a clear Trigger (e.g., new Gmail label).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Map incoming fields to &lt;code&gt;Set&lt;/code&gt; variables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add transformation Nodes (e.g., &lt;code&gt;Function&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connect to destination (Google Sheet, Slack, etc.).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure &lt;code&gt;Error Workflow&lt;/code&gt; and retries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Test with real data, then activate.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example mapping: Gmail → Sheet → Slack&lt;/strong&gt; – Imagine Alex, a marketing coordinator, who wants every “Lead” email saved to a Google Sheet and a Slack alert sent. The flow looks like:&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;Trigger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Gmail&lt;/span&gt; &lt;span class="err"&gt;–&lt;/span&gt; &lt;span class="nx"&gt;New&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Lead&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nb"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;extract&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;
&lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nx"&gt;Google&lt;/span&gt; &lt;span class="nx"&gt;Sheets&lt;/span&gt; &lt;span class="err"&gt;–&lt;/span&gt; &lt;span class="nx"&gt;Append&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;
&lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nx"&gt;Slack&lt;/span&gt; &lt;span class="err"&gt;–&lt;/span&gt; &lt;span class="nx"&gt;Post&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;leads&lt;/span&gt; &lt;span class="nx"&gt;channel&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tools &amp;amp; resources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;n8n.cloud – instant hosted instance, no Docker fuss.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Docker – &lt;code&gt;docker run -it n8nio/n8n&lt;/code&gt; for local sandbox.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Community Forum – quick answers, ready‑made snippets.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Official Docs – node reference and API limits.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep this sheet handy; the next repetitive task will automate itself.&lt;/p&gt;

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

&lt;p&gt;Grab the “Lead Capture” workflow, hit duplicate, swap the Gmail label, and point the Google Sheet node at a fresh spreadsheet – it’s like ordering a coffee just the way you like it, only once.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easy&lt;/strong&gt;: Open the workflow, click &lt;code&gt;Duplicate&lt;/code&gt;, change the &lt;code&gt;Gmail – New Email&lt;/code&gt; node’s label to &lt;code&gt;New Leads&lt;/code&gt;, and edit the &lt;code&gt;Google Sheets – Append&lt;/code&gt; node to reference &lt;code&gt;Leads_2024&lt;/code&gt;. Save and run – you’ve just created a brand‑new lead funnel without re‑typing anything.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Medium&lt;/strong&gt;: Add an &lt;code&gt;If&lt;/code&gt; node after the lead extraction step. Set the condition to &lt;code&gt;lead.value &amp;gt; 5000&lt;/code&gt;. Connect the “true” path to a &lt;code&gt;Slack – Send Message&lt;/code&gt; node and the “false” path to a &lt;code&gt;No Op&lt;/code&gt; node. Think of it as a GPS that only reroutes you when traffic exceeds a certain speed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Hard&lt;/strong&gt;: Spin up a VPS, install Docker, and run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; n8n &lt;span class="nt"&gt;-p&lt;/span&gt; 5678:5678 n8nio/n8n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. Inside n8n, create custom credentials for your internal APIs, then add a &lt;code&gt;Cron&lt;/code&gt; trigger set to &lt;code&gt;0 2 * * *&lt;/code&gt; and attach a workflow that deletes rows older than 30 days. This is like packing a suitcase for a month‑long trip – you plan every compartment in advance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat sheet&lt;/strong&gt;: Duplicate → Edit nodes → Save; If node → Condition → Slack; VPS → Docker → Cron.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: Keep a copy of your original workflow in a separate folder; it’s your safety net if a change breaks something.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💬 Got a workflow that’s stuck? Drop a comment below and I’ll help you troubleshoot!&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>n8n</category>
      <category>automation</category>
      <category>workflow</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Deploy Your First Smart Contract on Ethereum Mainnet – A Step‑by‑Step Guide</title>
      <dc:creator>Abdullah Sheikh</dc:creator>
      <pubDate>Thu, 21 May 2026 12:07:13 +0000</pubDate>
      <link>https://forem.com/-abdullah-sheikh/how-to-deploy-your-first-smart-contract-on-ethereum-mainnet-a-step-by-step-guide-6j6</link>
      <guid>https://forem.com/-abdullah-sheikh/how-to-deploy-your-first-smart-contract-on-ethereum-mainnet-a-step-by-step-guide-6j6</guid>
      <description>&lt;p&gt;&lt;em&gt;Deploy a real, production‑ready smart contract to Ethereum Mainnet in under an hour, even if you’ve only used testnets before&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 able to point a wallet at the Ethereum mainnet and press “Deploy” with confidence.&lt;/p&gt;

&lt;p&gt;You’ll know exactly which source files, .json artifacts, and &lt;strong&gt;network configuration&lt;/strong&gt; entries are required, just like checking a grocery list before heading to the store.&lt;/p&gt;

&lt;p&gt;You’ll actually run a live deployment using your tool of choice—Remix, Hardhat, or Foundry—and then verify the contract on Etherscan, similar to getting a receipt that proves you paid.&lt;/p&gt;

&lt;p&gt;Finally, you’ll master gas estimation, safely fund your deployment wallet, and troubleshoot the most common errors, as easily as fixing a typo before sending a text.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Identify the minimal set of files (&lt;code&gt;contracts/&lt;/code&gt;, &lt;code&gt;hardhat.config.js&lt;/code&gt;, etc.) and the exact &lt;strong&gt;network settings&lt;/strong&gt; needed for mainnet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run a deployment command, watch the transaction hash appear, and submit the source to Etherscan for verification.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Calculate the required gas, send just enough ETH to the deployer address, and use built‑in checks to catch errors before they cost you.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Remix:&lt;/strong&gt; Browser IDE, perfect for a one‑off deployment without any local setup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hardhat:&lt;/strong&gt; Full‑stack framework, ideal if you already use scripts or tests.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Foundry:&lt;/strong&gt; Fast, Rust‑based toolchain for developers who prefer command‑line speed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat Sheet – Files:&lt;/strong&gt; &lt;code&gt;MyContract.sol&lt;/code&gt;, &lt;code&gt;artifacts/&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt; with &lt;code&gt;MAINNET_URL&lt;/code&gt; and &lt;code&gt;PRIVATE_KEY&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat Sheet – Commands:&lt;/strong&gt; &lt;code&gt;npx hardhat run scripts/deploy.js --network mainnet&lt;/code&gt; or &lt;code&gt;forge script Deploy --rpc-url $MAINNET_URL --private-key $PRIVATE_KEY&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cheat Sheet – Verification:&lt;/strong&gt; npx hardhat verify --network mainnet  "constructorArg1".&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you’re set to move from testnet sandbox to real‑world mainnet deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Smart Contract Actually Is (No Jargon)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Smart contracts&lt;/strong&gt; are just pieces of code that live on Ethereum’s blockchain and run automatically when the rules you wrote are satisfied. There’s no middle‑person watching over them; the network itself guarantees they behave exactly as programmed.&lt;/p&gt;

&lt;p&gt;Imagine a digital vending machine. You insert a token, press a button, and the machine drops out the snack you chose—no cashier needed. A smart contract works the same way: it watches for a specific trigger (like a payment) and then executes the next step (sending the product or token) without any manual intervention.&lt;/p&gt;

&lt;p&gt;Another way to see it is as a programmable escrow. Two people agree on a condition—say, a freelance developer delivers code and the client releases the payment. The contract holds the ether, checks that the code was submitted, and then automatically transfers the funds. Both sides can trust the outcome because the contract’s logic is transparent and unchangeable once deployed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Self‑executing: no one has to click “send” after the condition is met.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stored on chain: the code is immutable and visible to anyone.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Trustless: the network enforces the rules, not a third party.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you finally &lt;em&gt;deploy smart contract ethereum mainnet&lt;/em&gt;, you’re essentially placing that vending machine or escrow into a public hallway where anyone can walk up, interact, and rely on its exact behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 Mistakes Everyone Makes With Ethereum Deployments
&lt;/h2&gt;

&lt;p&gt;Most developers hit a wall the first time they try to &lt;strong&gt;deploy smart contract ethereum mainnet&lt;/strong&gt; because they overlook three simple things.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Forgetting the correct compiler version.&lt;/strong&gt; Imagine ordering a pizza with the wrong size; the kitchen sends a small one that doesn’t fit your appetite. Using a mismatched &lt;code&gt;pragma solidity&lt;/code&gt; line produces bytecode the EVM can’t recognize, and the transaction reverts before it even lands.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Using a testnet‑only wallet on mainnet.&lt;/strong&gt; It’s like buying a train ticket for a route that doesn’t exist—you’ll never reach your destination, and the money is gone. A wallet address generated for Goerli or Sepolia lacks any ETH on the main chain, so any gas you send simply burns.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ignoring gas price spikes.&lt;/strong&gt; Think of it as driving during rush hour without checking traffic; you’ll either sit idle or pay premium tolls. When the network is congested, a low &lt;code&gt;maxFeePerGas&lt;/code&gt; leaves your deployment stuck in the mempool, while a wildly high bid wastes precious ETH.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep these pitfalls in mind, and the path to a successful mainnet launch becomes a lot clearer.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Deploy Your Smart Contract: Step‑by‑Step
&lt;/h2&gt;

&lt;p&gt;Let’s get your contract from folder to mainnet in nine concrete moves.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Install &lt;strong&gt;Node.js ≥ 20&lt;/strong&gt; and your favorite package manager (&lt;code&gt;npm&lt;/code&gt; or &lt;code&gt;yarn&lt;/code&gt;). Think of it as installing the kitchen tools before you start cooking.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Clone or initialise a Hardhat project, then drop your &lt;code&gt;.sol&lt;/code&gt; file into the &lt;code&gt;contracts/&lt;/code&gt; directory. It’s like placing the ingredients on the counter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Edit &lt;code&gt;hardhat.config.js&lt;/code&gt; to set &lt;code&gt;solidity: "x.y.z"&lt;/code&gt; matching the version you used locally. This tells Hardhat which recipe to follow.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file and add two lines:&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="nv"&gt;MAINNET_PRIVATE_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your‑private‑key
&lt;span class="nv"&gt;INFURA_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your‑infura‑or‑alchemy‑key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are your wallet and the bridge to Ethereum, just like a passport and a flight ticket.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;npx hardhat compile&lt;/code&gt;. Hardhat will output bytecode and an ABI in the &lt;code&gt;artifacts/&lt;/code&gt; folder, ready for shipping.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Estimate gas to avoid surprise fees: &lt;code&gt;hardhat run --network mainnet scripts/estimate-gas.js&lt;/code&gt;. It’s the Google Maps “estimate time” before you start driving.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deploy with &lt;code&gt;npx hardhat run --network mainnet scripts/deploy.js&lt;/code&gt;. This broadcasts the transaction, similar to sending a package with a tracking number.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Verify on Etherscan so others can read the source: &lt;code&gt;hardhat verify --network mainnet&lt;/code&gt;. Verification is like stamping a sealed envelope.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Save the transaction hash and contract address in a secure spreadsheet. Treat it like the receipt you keep after buying something expensive.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now you have a live contract and a record of every step—ready for the next phase.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Real Example: Deploying a Simple Token for “EcoFund”
&lt;/h2&gt;

&lt;p&gt;Maya clicks “Deploy” in her terminal just like confirming a food order on her phone.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compile the contract.&lt;/strong&gt; She runs &lt;code&gt;npx hardhat compile&lt;/code&gt; and watches the console confirm “Compilation finished” – no errors, like a receipt that says everything’s in the basket.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Run the deployment script on mainnet.&lt;/strong&gt; Using the command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hardhat run &lt;span class="nt"&gt;--network&lt;/span&gt; mainnet scripts/deployEco.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hardhat pulls the &lt;code&gt;.env&lt;/code&gt; value with Maya’s MetaMask private key, signs the transaction, and broadcasts it. At a gas price of 45 gwei the network charges roughly 0.045 ETH, similar to paying a small tip for a quick coffee.&lt;br&gt;
&lt;strong&gt;Verify on Etherscan.&lt;/strong&gt; After the tx is mined, Maya copies the contract address and runs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx hardhat verify &lt;span class="nt"&gt;--network&lt;/span&gt; mainnet &lt;span class="nv"&gt;$CONTRACT_ADDRESS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;. Etherscan shows the source code, so anyone can read the token’s rules – like a menu that lets diners see every ingredient.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Share the address.&lt;/strong&gt; She pins the address in the DAO’s Discord channel and adds it to the “Resources” board, ensuring members can find it as easily as a bookmarked map location.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Result.&lt;/strong&gt; The &lt;strong&gt;ECO&lt;/strong&gt; token is live, appears on Uniswap, and DAO members can start staking right away.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tip.&lt;/strong&gt; Keep a small “gas buffer” ETH in the deploying wallet; it prevents the transaction from failing midway, just like keeping extra cash for a parking meter.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now Maya’s sustainability DAO has a real token to power its mission, and she’s ready for the next smart‑contract adventure.&lt;/p&gt;

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

&lt;p&gt;Grab the right toolbox and the mainnet deployment feels as painless as ordering a pizza with preset toppings.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hardhat 2025&lt;/strong&gt; – the go‑to framework for Ethereum projects. It compiles, runs tests, and spins up a local node just like a kitchen prep station, letting you tweak gas estimates before the real order hits the oven.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Remix IDE (web)&lt;/strong&gt; – a browser‑based editor that compiles and deploys in one click. Think of it as a drive‑through: you drop your contract in, hit “Deploy,” and MetaMask hands over the transaction without leaving the page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Etherscan Verify API&lt;/strong&gt; – automates source‑code verification after deployment. It’s the receipt printer at the checkout, confirming every detail for the blockchain’s record keeper.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alchemy Supernode&lt;/strong&gt; – a reliable Mainnet RPC with a free tier of 1 M requests per day. Consider it the GPS for your calls; it routes traffic efficiently, preventing “lost‑in‑translation” errors that cheap nodes often cause.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MetaMask Flask&lt;/strong&gt; – the developer‑focused fork of MetaMask. It bundles a gas‑tracker and one‑click network switching, like a multi‑tool that lets you measure fees while you’re packing your deployment suitcase.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these tools you can &lt;code&gt;deploy smart contract ethereum mainnet&lt;/code&gt; without building a full DevOps pipeline. Hardhat handles compilation, Remix gives you a quick sanity check, Alchemy guarantees the RPC connection stays stable, MetaMask Flask shows you the exact gas cost, and Etherscan Verify seals the deal with a public audit.&lt;/p&gt;

&lt;p&gt;Pick the combo that matches your workflow, and the rest of the process will fall into place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference: Ethereum Mainnet Deployment Cheat Sheet
&lt;/h2&gt;

&lt;p&gt;Grab this list, follow it, and you’ll have a contract on mainnet without a surprise bill.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prereqs&lt;/strong&gt;: Node 20+, npm, MetaMask mainnet wallet loaded with ETH for gas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Setup&lt;/strong&gt;: Run &lt;code&gt;npm init -y &amp;amp;&amp;amp; npm i --save-dev hardhat&lt;/code&gt;, then &lt;code&gt;npx hardhat&lt;/code&gt; to scaffold a project and add your contract file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Config&lt;/strong&gt;: Edit &lt;code&gt;hardhat.config.js&lt;/code&gt; – set the Solidity version and add a &lt;code&gt;mainnet&lt;/code&gt; network pointing at Alchemy or Infura (think of it as choosing the right highway for your trip).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Env variables&lt;/strong&gt; (keep them secret): create a &lt;code&gt;.env&lt;/code&gt; with &lt;code&gt;MAINNET_PRIVATE_KEY&lt;/code&gt; and &lt;code&gt;ALCHEMY_API_KEY&lt;/code&gt;, then add &lt;code&gt;.env&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compile&lt;/strong&gt;: &lt;code&gt;npx hardhat compile&lt;/code&gt;. &lt;em&gt;Alice&lt;/em&gt; once compiled her token and saw the &lt;code&gt;artifacts&lt;/code&gt; folder appear – that’s your finished dish ready to be served.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Estimate gas&lt;/strong&gt;: Use &lt;code&gt;await ethers.provider.getFeeData()&lt;/code&gt; or a block explorer to see current prices and set &lt;code&gt;gasPrice&lt;/code&gt; a bit above average to avoid stuck txs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deploy&lt;/strong&gt;: &lt;code&gt;npx hardhat run --network mainnet scripts/deploy.js&lt;/code&gt;. This command is the “order now” button that sends your contract to the kitchen.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verify&lt;/strong&gt;: &lt;code&gt;npx hardhat verify --network mainnet&lt;/code&gt;. Verification is like attaching a receipt so anyone can see what you shipped.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Record&lt;/strong&gt;: Save the transaction hash, contract address, and the Etherscan link. Treat it like keeping a passport stamp – you’ll need it later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep private keys offline after deployment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Monitor gas fees for the first 24 h; adjust if the network spikes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bookmark the Etherscan page for quick reference.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Here’s the three‑step ladder you can climb right after your contract goes live.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Easy – Show the address on your site.&lt;/strong&gt; Plug the new contract address into a blockchain explorer widget the same way you embed a Google Maps pin on a contact page. Visitors can instantly verify the deployment and see transaction history.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Medium – Build a tiny UI.&lt;/strong&gt; Create a single‑page React app that calls one contract function with &lt;code&gt;ethers.js&lt;/code&gt;. Think of it like ordering a coffee: you only need a button for “Buy” instead of a full menu. The app proves the contract works for real users without extra fluff.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hard – Automate with CI/CD.&lt;/strong&gt; Set up a GitHub Actions workflow that runs your test suite and pushes a new deployment when you tag a commit. It’s like packing a suitcase that locks itself when you zip it – the process repeats reliably without manual steps.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tools you’ll need:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Explorer widget: &lt;code&gt;https://etherscan.io/widget&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;React + ethers: &lt;code&gt;npm install ethers@5 react&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;GitHub Actions: &lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Quick cheat sheet:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;npm run build&lt;/code&gt; – bundle front‑end&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;npx hardhat run scripts/deploy.js --network mainnet&lt;/code&gt; – manual deploy&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;git tag v1.0 &amp;amp;&amp;amp; git push origin v1.0&lt;/code&gt; – trigger auto‑deploy&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;v*'&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npx hardhat run scripts/deploy.js --network mainnet&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;PRIVATE_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PRIVATE_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which of these steps are you most excited to try first?&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>ethereum</category>
      <category>smartcontracts</category>
      <category>blockchaindevelopment</category>
      <category>devops</category>
    </item>
    <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://forem.com/-abdullah-sheikh/how-to-set-up-a-self-hosted-server-on-oracle-cloud-free-tier-in-30-minutes-1nh5</link>
      <guid>https://forem.com/-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://forem.com/-abdullah-sheikh/how-to-build-a-rest-api-with-nodejs-and-express-from-scratch-5d16</link>
      <guid>https://forem.com/-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>
