Monorepo: Express + MongoDB + JWT API and Next.js (App Router) frontend with role-based access (ADMIN, HR, MANAGER, EMPLOYEE).
- Node 20+
- MongoDB 7+ (local or Docker)
cp backend/.env.example backend/.env
# Edit backend/.env — set JWT secrets (32+ chars) and MONGO_URI
npm install
# Terminal 1 — API
npm run dev -w backend
# Terminal 2 — UI (proxies /api to the backend via next.config rewrites)
npm run dev -w frontendSeed demo users and HR/manager/employee (see backend/src/scripts/seed.ts):
npm run seed -w backendOpen http://localhost:3000. Example logins after seed:
| Role | Password | |
|---|---|---|
| Admin | admin@example.com | Admin123! |
| HR | hr@example.com | Hr123456! |
| Manager | manager@example.com | Manager123! |
| Employee | employee@example.com | Employee123! |
The UI calls the API through same-origin
/api/...so the httpOnly refresh cookie is set onlocalhost:3000. SetBACKEND_URLinfrontend/.env.localif the API is not on port 4000.
docker compose up --buildThen run seed inside the backend container once:
docker compose exec backend npm run seed:prodOr from the host against local Mongo (MONGO_URI=mongodb://127.0.0.1:27017/ems in backend/.env):
npm run seed -w backend| Command | Description |
|---|---|
npm run dev |
API + UI concurrently |
npm run build |
Build both workspaces |
npm test |
Backend Jest (health) |
npm run e2e |
Frontend Playwright smoke |
- Access token: short-lived JWT in memory on the client (Axios default header).
- Refresh token: httpOnly cookie (
refresh), rotated on each refresh; stored server-side as SHA-256 hash on the user document. - Authorization: enforced in Express middleware; the UI mirrors permissions for navigation only.
See feature-x.md for the full step-by-step implementation map.
MIT