From Fruit Stand to Empire: Adding PostgreSQL to Your NextJS App
In the year 1206, a man named Temüjin united the warring tribes of the Mongolian steppe and declared himself Genghis Khan, the “Universal Ruler.” He had a vision. Not just a tent here and a horse there, but an empire. The kind of thing that stretches from Korea to Hungary and makes cartographers weep.
But Genghis didn’t do it alone. He had four generals so fierce, so loyal, and so terrifyingly competent that he called them his Four Dogs of War, the Dörvön Nokhod. Their names were Subutai, Jebe, Jelme, and Khubilai.
These four didn’t just show up one day fully formed as military geniuses. They grew up together on the steppe, learned to ride before they could walk, and built something that every successful team needs: mutual trust forged through shared hardship.
What does this have to do with adding PostgreSQL to a Next.js app?
Everything.
Previously on Fruit Store: The Beautiful Lie
In the previous article, we built a Fruit Store. It was beautiful. It was fast. It was deployed to Azure. Customers could browse apples, add bananas to their cart, and feel the warm glow of modern e-commerce.
But it was a Potemkin village of vitamins.
The fruits were hardcoded. A static array sitting in src/data/fruits.ts, pretending to be a real product catalog. It was the software equivalent of a market stall with plastic fruit glued to the table. You could look, but nothing was real. In the world of the steppe, this is like having a thousand horses made of straw: they look impressive from a distance, but they won’t carry you to Samarkand.
At the end of that article, we promised a next step: a real database. Today we stop pretending. Today, we build the supply lines.
The Four Dogs of Architecture
To turn a market stall into an empire, we need to deploy our own Dörvön Nokhod. We aren’t just “adding a database”; we are refactoring our entire philosophy of data.
1. Subutai the Strategist (PostgreSQL)
Subutai was the son of a reindeer herder. He didn’t come from a “royal” bloodline, but he had a mind that could hold an entire continent’s worth of supply lines in his head. In our stack, PostgreSQL is Subutai. It isn’t the trendy “NewSQL” database of the week. It doesn’t have a VC-funded marketing team promising it will “reimagine the cloud.” It just works, reliably, since 1996. It is the boring, devastatingly effective foundation of our empire.
2. Jebe the Arrow (Server Components)
Jebe was the legendary scout who could shoot an arrow with such speed and precision that it felt like magic. In Next.js, Server Components are Jebe. Most frameworks make you take a long, winding detour: the browser calls the API, the API calls the database, the database returns data, the API returns JSON, and the browser finally renders. Jebe doesn’t have time for that bureaucracy. He strikes the database directly and delivers the result straight to the UI.
3. Jelme the Protector (Docker)
Jelme once saved Genghis Khan’s life by sucking poison from a neck wound and covering him with his own body during a freezing night. Docker is Jelme. It wraps your application in a protective shell, isolating it from the pathogens of “well, it worked on my machine.” It ensures that whether your app is on a MacBook in San Francisco or a Linux server in Northern Europe, it feels exactly the same.
4. Khubilai the Expander (Azure Container Apps)
Khubilai pushed the empire into new territories, establishing control where no steppe rider had ever been. Azure Container Apps is our Khubilai. It takes our Dockerized army and scales it across the global cloud infrastructure, managing the heavy lifting of hosting so we can focus on the conquest.
Subutai: Forging the Database
We start with the foundation. No ORM. No “magic” abstraction layers that hide the SQL. Subutai didn’t use fancy equipment; give him a horse and a map and he’d conquer a kingdom. We’ll use the pg library—raw, powerful, and honest.
Installing the Client
The Connection Pool: Avoid the Horde
In development, Next.js loves to hot-reload. If you create a new database connection every time you save a file, you’ll exhaust your PostgreSQL connections faster than a Mongol horde exhausts a watering hole. We use the globalThis trick to keep the connection alive across reloads:
Steppe Wisdom: A single connection is a scout. A connection pool is a vanguard. Never send a scout to do a vanguard’s job.
The Seed Script: Planting the Empire
Every empire needs an origin story. Ours starts with a table and eight fruits. We use ON CONFLICT (id) DO UPDATE because in the world of data, idempotency is the difference between a stable empire and a chaotic uprising.
Jebe: The Direct Strike
Now for the “Jebe” move. We are going to query the database directly inside our Next.js Server Components. No useEffect, no fetch('/api/fruits'), no loading spinners that make your users feel like they’re waiting for a slow caravan.
The Query Layer
The Page: Zero-Latency Feel
The page.tsx becomes async. It awaits the data on the server. By the time the HTML reaches the user’s browser, the fruits are already there.
tsx // src/app/page.tsx export default async function Home() { const fruits = await getFruits(); // The arrow hits the target before the UI even renders.
return ( <main> {/* … map through fruits … */} </main> ); }
Jelme: The Docker Armor
A server that runs on your laptop is a nomad. A server that runs in Docker is an army. By setting output: "standalone" in next.config.ts, we tell Next.js to package only the files needed for production. No node_modules bloat. No source code leakage. Just the lean, armored core of our application.
Our Dockerfile uses a three-stage build: Dependencies, Builder, and Runner. The final image is a tiny, hardened vessel that only knows how to do one thing: serve fruit.
The Hangover: The Cost of Conquest
Moving from a static site to a database-backed application is the moment the “Empire” gets real. It’s also the moment things get complicated.
The Beautiful Lie of Static was easy. You build it, you push it, it lives forever on a CDN. It’s cheap. It’s indestructible.
The Brutal Truth of Dynamic means:
- Migrations: You can’t just change a TypeScript interface; you have to talk to Subutai and tell him the schema is changing.
- Cold Starts: If your container spins down, the first user might wait a second for the engine to warm up.
- Secrets: You have to manage
DATABASE_URLlike it’s the location of the Khan’s treasury. If it leaks, you’re finished.
The Result: A Living System
The Four Dogs worked because they were a system. Subutai’s data is useless without Jebe’s speed. Jebe’s speed is useless without Jelme’s protection. And it all needs Khubilai to scale.
We’ve replaced our straw horses with real, breathing steeds. The Fruit Store is no longer a market stall; it’s a node in a global supply chain. The SQL runs on the server, the Docker image guards the code, and Azure provides the territory.
The hardest part wasn’t the code. It was deciding that the market stall wasn’t enough.
Running the Campaign
The full source code is on GitHub: develprr/fruit-store-nextjs
To join the horde:
- Clone the repo.
- Spin up PostgreSQL with Docker.
- Run
npm run seed. - Run
npm run dev.
The empire is just getting started. 🐎