Project Overview & Prisma Setup

Food Tracker (React + Express + Prisma)

Chapter 1 · Project Overview & Prisma Setup

This course is a direct companion to Food Tracker (React + Express) — the same app, the same React-plus-Express architecture, built a second time with one deliberate change: Prisma in place of raw SQL via better-sqlite3. That sibling course made "deliberately no ORM" its own honest choice; this one exists specifically to show what an ORM changes, and what it costs, for someone who already knows SQL well and wants real hands-on experience with the other side of that tradeoff.

What the App Actually Does

The spec is identical to the sibling course's own — this course is not re-deciding what the app does, only how its data layer is written:

  • Scan a barcode with a phone or webcam camera, looking it up against Open Food Facts (free, open, no API key) to pull in the product's name and details automatically.
  • Record a use-by date for the item, and see it flagged once it's expiring soon.
  • Keep a full history of every item ever added — some still active with a real expiry date, some already marked used with none — searchable in real time as you type.
  • Look up recipes via TheMealDB (also free, no key) using ingredients close to expiring.

A weekly meal planner stays explicitly out of scope, matching every other Food Tracker course on this site.

Why This Course Exists: SQL You Already Know vs. an ORM You Don't

Food Tracker (React + Express) already makes the "same language, both ends" case for a Node/Express backend — that argument doesn't need repeating here, since this course shares it unchanged. What this course adds is a second, narrower comparison sitting entirely inside that same architecture: raw, hand-written SQL against Prisma's schema-first, generated-client approach to the exact same database.

AspectFood Tracker (React + Express)This course
Schema definitionHand-written CREATE TABLE SQL in schema.sqlA schema.prisma model, one field per line, no SQL DDL written by hand
Applying schema changesManual ALTER TABLE statements — no migrations systemPrisma Migrate — every schema change becomes a real, versioned migration file, applied with npx prisma migrate dev
QueryingRaw SQL strings with ? placeholders, run directly through better-sqlite3Prisma Client's generated query methods — create(), findMany(), update() — built from the schema itself
Sync vs. asyncDeliberately synchronous — better-sqlite3 is unusual among Node DB libraries in having no async/awaitFully async/await, matching how most Node database tools actually work
Dev toolingNone beyond the SQL itselfnpx prisma studio — a real, local GUI for browsing and editing the database
The one-sentence version of this whole course
Every route this course writes will look and behave differently from its sibling's own version of the same route — not because the app changed, but because Prisma changes how you talk to the database, from hand-written, synchronous SQL strings to generated, asynchronous, schema-derived method calls.

The Real Cost of an ORM, Named Honestly

Food Tracker (React + Express) names its own honest cost plainly: skipping an ORM means giving up migrations and a query builder, in exchange for total transparency about what SQL is actually running. Prisma's own honest cost runs the other way. Prisma Client's generated methods hide the actual SQL being executed — genuinely convenient day to day, but it's exactly the kind of abstraction that can let an N+1 query problem creep in unnoticed: a loop that looks like ordinary JavaScript can quietly issue one database round-trip per iteration instead of one query total, with nothing in the code itself flagging it. This course will call that out directly wherever it's a real risk, rather than presenting Prisma as free convenience with no downside.

Scaffolding the Project

The client side is identical to the sibling course — a Vite-powered React app, unaffected by what the backend does with its database:

# the React client — same as Food Tracker (React + Express) npm create vite@latest client -- --template react cd client && npm install

The server side starts the same way too, then adds Prisma on top:

# the Express server, in a sibling folder cd .. mkdir server && cd server npm init -y npm install express cors dotenv # Prisma itself — a dev-time CLI, plus the client your route code actually imports npm install prisma --save-dev npm install @prisma/client # scaffolds prisma/schema.prisma and a .env file, wired for SQLite npx prisma init --datasource-provider sqlite

prisma init generates a near-empty starting schema — no Item model yet, since Chapter 2 is where the real data modeling happens:

// prisma/schema.prisma generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") }

And the accompanying .env file, pointing Prisma at a local SQLite file the same way better-sqlite3 pointed at foodtracker.db in the sibling course:

# .env DATABASE_URL="file:./dev.db"

A minimal server, confirming everything works end to end before any real feature — or any actual database query — exists yet:

// server/index.js import express from "express"; import cors from "cors"; const app = express(); app.use(cors()); app.use(express.json()); app.get("/api/health", (req, res) => { res.json({ status: "ok" }); }); const PORT = process.env.PORT || 3001; app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
CORS is not optional during development — unchanged from the sibling course
Vite's dev server and Express run on two different ports (typically 5173 and 3001), which the browser treats as two different origins. Without cors(), every request from the React app to the Express API is blocked outright. Prisma doesn't touch this at all — it's purely a client/server-port issue, included here for completeness since this chapter may be read on its own without the sibling course alongside it.
Two dev-time tools worth running early
node --watch server/index.js restarts Express automatically on save, the same convenience Vite already gives the React side. And once Chapter 2 adds a real model, npx prisma studio opens a local, in-browser GUI for the actual SQLite database — genuinely useful for checking that a route did what it was supposed to, without writing a single SQL query to look.

Where This Course Is Headed

Data modeling with a real schema.prisma model and Prisma Migrate, barcode lookup with a Prisma-backed cache table, the camera-scanning React component (reused essentially unchanged — it's frontend-only, with no database layer involved), the add-item flow, expiry alerts built on Prisma's typed DateTime queries, item history with live search via Prisma's contains filter, marking items used, recipe lookup, cross-cutting state management, deployment (with Prisma's own build-time generation step added), and a capstone tying every chapter into one complete, working app — closing with an honest comparison back to the sibling course's own raw-SQL version.

Hands-On Exercises

Exercise 1

In one sentence, state this course's own core comparison claim against Food Tracker (React + Express). Then, using this chapter's own "Real Cost of an ORM" section, name the one concrete risk Prisma introduces that raw SQL doesn't have.

📄 View solution
Exercise 2

Explain the sync-vs-async difference between better-sqlite3 and Prisma Client, and why it means a Prisma-backed route handler has to be written differently from its sibling's own version — even though both routes do the same thing.

📄 View solution
Exercise 3

Using this chapter's own compare-table, explain what problem Prisma Migrate actually solves that the sibling course's own "manual ALTER TABLE statements" approach doesn't.

📄 View solution

Chapter 1 Quick Reference

  • The shared app — barcode scan (Open Food Facts) → expiry tracking → alerts → searchable history → recipe lookup (TheMealDB); no meal planner — identical spec to Food Tracker (React + Express)
  • This course's own throughline: the same app, the same React + Express architecture, with Prisma replacing raw SQL via better-sqlite3
  • Real payoff: generated, type-shaped queries; Prisma Migrate's real, versioned schema changes; a GUI database browser via npx prisma studio
  • Honest cost: the actual SQL is hidden behind Prisma Client, which is exactly what makes an N+1 query problem easy to introduce without noticing
  • Setup: the same Vite React client as the sibling course; an Express server with prisma and @prisma/client installed, prisma/schema.prisma scaffolded via npx prisma init --datasource-provider sqlite
  • Next chapter: Data Modeling with Prisma: schema.prisma & Prisma Migrate