SkillShare Logo
STEP 2 OF 7 · SCAFFOLD

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.

01 — STRUCTURE

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/
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
create the folder
$ mkdir inventory-app
$ cd inventory-app
Next.js note
Next.js can host the API itself via Route Handlers, so a separate server/ 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.
02 — FRONTEND

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.

create the frontend
$ npx create-next-app@latest web
# TypeScript? Yes · ESLint? Yes · Tailwind CSS? Yes · App Router? Yes
$ cd web && npm run dev   # → http://localhost:3000

Tailwind is wired up automatically. State (the logged-in user, the product list) will live in Zustand:

web/
$ npm install zustand
Optional polish
Add shadcn/ui for ready-made buttons, dialogs, and tables: npx shadcn@latest init. The components are copied into your project, so you own and can edit them.
03 — BACKEND

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.

create the backend
$ mkdir server && cd server
$ npm init -y
$ npm install express
$ npm install -D typescript tsx @types/express @types/node
$ npx tsc --init   # creates tsconfig.json

A minimal server to confirm it runs:

server/src/index.ts
import express from "express";
const app = express();
app.use(express.json());

app.get("/api/health", (_req, res) => res.json({ status: "ok" }));

app.listen(4000, () => console.log("API on http://localhost:4000"));
server/package.json → scripts
"scripts": {
  "dev": "tsx watch src/index.ts",
  "start": "node dist/index.js"
}
04 — PACKAGES

Install the Core Packages

These power authentication, validation, email, and database access. Run inside server/.

Shared by every stack

server/
$ npm install bcryptjs jsonwebtoken zod nodemailer
$ npm install -D @types/bcryptjs @types/jsonwebtoken @types/nodemailer
  • bcryptjs — hash passwords safely.
  • jsonwebtoken — sign/verify JWTs (login + email-verify tokens).
  • zod — validate incoming request bodies.
  • nodemailer — send verification & low-stock emails over SMTP.

Express adds

Express needs a few middlewares that NestJS already bundles:

server/ (Express only)
$ npm install cors cookie-parser dotenv
$ npm install -D @types/cors @types/cookie-parser

Database driver — pick one

Postgres uses TypeORM; MongoDB uses Mongoose. We install the full setup on the next page; the packages are:

Express → TypeORM
$ npm install typeorm reflect-metadata pg
NestJS → TypeORM
$ npm install @nestjs/typeorm typeorm pg
$ npm install @nestjs/config @nestjs/jwt
05 — CONFIG

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.

server/.env
# --- App ---
PORT=4000
CLIENT_URL="http://localhost:3000"   # 5173 for Vite

# --- Database (use ONE) ---
DATABASE_URL="postgresql://postgres:secret@localhost:5432/inventory"
# DATABASE_URL="mongodb+srv://<username>:<password>@cluster0.xxxx.mongodb.net/inventory?retryWrites=true&w=majority"

# --- Auth ---
JWT_SECRET="a-long-random-string-change-me"

# --- SMTP (from Mailtrap or Gmail App Password) ---
SMTP_HOST="sandbox.smtp.mailtrap.io"
SMTP_PORT=2525
SMTP_USER="your-user"
SMTP_PASS="your-pass"
MAIL_FROM="Inventory MS <no-reply@inventory.app>"
Never commit secrets
Create a .gitignore with node_modules and .env in it. Commit a .env.example (same keys, blank values) so teammates know what to fill in.
06 — CHECKPOINT

Run Both Dev Servers

Open two bash terminals. Frontend in one, backend in the other.

bash
# bash 1
$ cd web && npm run dev

# bash 2
$ cd server && npm run dev
  • Frontend loads (localhost:3000 or :5173)
  • Backend responds at /api/health with {"status":"ok"}
  • All core packages installed without errors
  • .env created and git-ignored