Project Setup
Create the two halves of the app — a frontend (the UI) and a backend(the API) — then install every package you'll need. Pick the tab that matches your stack choice.
One Repo, Two Apps
We keep the frontend and backend side-by-side in a single folder. The database is set up on the next page (Docker for Postgres, Atlas for MongoDB).
inventory-app/
├─ web/ # frontend — Next.js OR React + Vite
├─ server/ # backend — Express OR NestJS
├─ docker-compose.yml # Runs Postgres & services (added in Step 7)
└─ README.md$ mkdir inventory-app
$ cd inventory-appserver/ is optional. This guide keeps a standalone backend so the same API works for both Next.js and React + Vite. If you prefer Next-only, put the backend code in web/app/api/* instead — the logic is identical.Create the Frontend
Both options use React under the hood. Next.js (nextjs.org) is a full framework; React + Vite (vite.dev) is a minimal SPA (single-page app) setup.
$ npx create-next-app@latest
# ✔ What is your project named? web
# ✔ Would you like to use the recommended Next.js defaults? › No, customize settings
# ✔ Would you like to use TypeScript? Yes
# ✔ Which linter would you like to use? ESLint
# ✔ Would you like to use React Compiler? Yes
# ✔ Would you like to use Tailwind CSS? Yes
# ✔ Would you like your code inside a `src/` directory? Yes
# ✔ Would you like to use App Router? (recommended) Yes
# ✔ Would you like to customize the import alias (@/* by default)? No
# ✔ Would you like to include AGENTS.md to guide coding agents to write up-to-date Next.js code? Yes
$ cd web
$ npm run dev # → http://localhost:3000Tailwind is wired up automatically. State (the logged-in user, the product list) will live in Zustand:
$ npm install zustandnpx shadcn@latest init. The components are copied into your project, so you own and can edit them.Create the Backend
The API holds all the real logic — auth, validation, database access, email. Express (expressjs.com) is minimal; NestJS (nestjs.com) is structured.
$ npm i -g @nestjs/cli
$ nest new server # choose npm when asked
$ cd server && npm run start:dev # → http://localhost:3000Nest gives you controllers, services, and modules out of the box. Generate the feature modules you'll fill in later:
$ nest g resource auth
$ nest g resource products
# pick "REST API" and "Yes" to generate CRUD entry points3000, which clashes with Next.js. Change Nest to 5000 in main.ts (await app.listen(5000)) so both can run together (and align with the port used by our frontend proxy).Install the Core Packages
These power authentication, validation, email, and database access. Run inside server/.
Shared by every stack
# For Express:
$ npm install bcryptjs jsonwebtoken express-validator nodemailer
$ npm install -D @types/bcryptjs @types/jsonwebtoken @types/nodemailer
# For NestJS (uses class-validator + bcrypt):
$ npm install bcrypt nodemailer
$ npm install -D @types/bcrypt @types/nodemailer- bcryptjs — hash passwords safely.
- jsonwebtoken — sign/verify JWTs (login + email-verify tokens).
- express-validator — validate incoming request bodies (Express); NestJS uses
class-validatorvia DTOs. - nodemailer — send verification & low-stock emails over SMTP.
Database driver — pick one
Postgres uses TypeORM; MongoDB uses Mongoose. We install the full setup on the next page; the packages are:
$ npm install typeorm pg # PostgreSQL + TypeORM (skip if using MongoDB)$ npm install @nestjs/typeorm typeorm pg
$ npm install @nestjs/config @nestjs/jwt @nestjs/passport passport passport-jwt class-validator class-transformer
$ npm install -D @types/passport-jwtEntitySchema definitions, which require no decorator configuration or build pipeline.Environment Variables
Secrets (DB password, JWT secret, SMTP login) never go in code. Put them in a .env file in server/ and add .env to .gitignore.
PORT=5000
MONGODB_URI=mongodb+srv://<user>:<password>@cluster.mongodb.net/inventory?retryWrites=true&w=majority
JWT_SECRET=your_super_secret_jwt_key_minimum_32_characters
JWT_EXPIRES_IN=7d
# Mailtrap SMTP (get from mailtrap.io → Email Testing → SMTP Settings)
EMAIL_HOST=sandbox.smtp.mailtrap.io
EMAIL_PORT=2525
EMAIL_USER=your_mailtrap_user
EMAIL_PASS=your_mailtrap_pass
EMAIL_FROM="Inventory Express <noreply@inventoryexpress.com>"
CLIENT_URL=http://localhost:5173
STORE_OWNER_EMAIL=your@email.com.gitignore with node_modules and .env in it. Commit a .env.example (same keys, blank values) so teammates know what to fill in.Run Both Dev Servers
Open two bash terminals. Frontend in one, backend in the other.
# terminal 1 — frontend (http://localhost:5173)
$ cd web && npm run dev
# terminal 2 — backend (http://localhost:5000)
$ cd server && npm run dev- ✓Frontend loads at
http://localhost:5173 - ✓Backend responds at
http://localhost:5000/api/healthwith{"status":"ok"} - ✓All core packages installed without errors
- ✓
.envcreated inserver/and git-ignored