Deployment
Food Tracker (React + Express + Prisma)
Chapter 11 · Deployment
Every prior chapter ran two separate processes — Vite's dev server and Express — talking across the CORS boundary Chapter 1 set up. A real deployment collapses that back down to one. Most of that story is identical to the sibling course; Prisma adds two real, extra steps to the pipeline that better-sqlite3 never needed.
Building the Client
Serving the Build From Express
app.get("*", ...) matches every path, including /api/items. If the catch-all were registered before the API routers, every single API request would be swallowed by it, silently returning index.html instead of JSON. The API routes must always be registered first — nothing about Prisma changes this; it's an Express-level fact.
React Router handles navigation entirely in the browser — a URL like /history never actually exists as a real file on the server. app.get("*", ...) always returns index.html instead of a 404, letting React Router take over and render the correct view client-side once the page loads.
Prisma's Own Real Deployment Steps
Neither of these exists for the sibling course, and skipping either one produces a real, working-locally-but-broken-on-the-server failure:
node_modules — and node_modules is never committed to version control. A fresh npm install on the deploy server rebuilds node_modules from scratch, which means the generated client has to be rebuilt there too. The @prisma/client package does include a postinstall hook that usually runs prisma generate automatically after npm install — but environments that skip install scripts (a Docker multi-stage build, certain CI caching setups run with --ignore-scripts) can silently skip it. Running prisma generate explicitly, rather than trusting the hook alone, is the defensive, honest version of this step.
prisma migrate dev, used throughout this course's own local development, can create new migrations and, in some drift scenarios, prompt to reset the development database — genuinely useful behavior while building, genuinely dangerous in production. prisma migrate deploy only applies migrations that already exist as committed files in prisma/migrations/; it never generates a new one, and it never prompts. It's the one Prisma command actually meant to run against a real, populated database.
| Deployment step | Sibling course | This course |
|---|---|---|
| Build the client | npm run build | Same |
| Prepare the backend | Nothing extra — better-sqlite3 needs no separate step | npx prisma generate |
| Apply the schema | Already applied — db.exec(schema.sql) runs at server startup | npx prisma migrate deploy |
| Start the process | pm2 start server/index.js | Same |
Environment Configuration and Process Management
A plain node server/index.js process exits the moment it crashes, and doesn't restart on its own. A process manager like pm2 keeps the server running, restarting it automatically on a crash or a server reboot:
TLS Termination
Chapter 4's own getUserMedia requires HTTPS in production — no exception. Express itself doesn't handle TLS certificates; the standard approach is a reverse proxy (nginx) sitting in front of the Node process, terminating HTTPS and forwarding plain HTTP internally. This has nothing to do with Prisma either way.
db.js, but the DATABASE_URL value in .env. Confirming the deployment target keeps whatever path DATABASE_URL points at across restarts is the same real, easy-to-overlook step either course needs — it just needs checking in a different file here.
Where This Course Is Headed
One chapter left: a capstone tying every chapter into one complete, working app.
Hands-On Exercises
Explain exactly what would happen to a request for /api/items if the catch-all route were registered before the API routers instead of after, and why no error would appear anywhere to signal the problem.
📄 View solutionExplain the real difference between prisma migrate dev and prisma migrate deploy, and why only the second one belongs in a deployment script.
📄 View solutionExplain why this chapter runs npx prisma generate explicitly on the deploy server rather than relying solely on @prisma/client's own postinstall hook.
📄 View solutionChapter 11 Quick Reference
- Build: npm run build produces client/dist/, served via express.static — identical to the sibling
- Order matters: API routes must be registered before the catch-all — an Express fact, unrelated to Prisma
- Two real extra steps: npx prisma generate (rebuild the generated client on this environment) and npx prisma migrate deploy (apply committed migrations, production-safe)
- Don't confuse the two migrate commands: migrate dev can create migrations and reset the dev database; migrate deploy only applies what's already committed, never prompts
- Process management & TLS: pm2 and an nginx reverse proxy, identical to the sibling
- Real gotcha, relocated: the SQLite file still needs a persistent volume — now tracked via DATABASE_URL in .env rather than a hardcoded path
- Next chapter: Capstone