A production-style MERN application where Admins manage users and projects, and Users view the projects assigned to them and update their progress. Built with a Next.js frontend and an Express + Prisma + MongoDB backend, both deployed to Vercel.
- Frontend (live): https://troology-two.vercel.app
- Backend (live): https://troology-backend.vercel.app — health check:
/health - Live API docs: https://troology-backend.vercel.app/docs (importable Postman collection at
/docs/collection.json) - API base path:
/api/v1
Default admin (after seeding):
admin@troology.com/Admin@123
| Area | What's implemented |
|---|---|
| Auth & RBAC | JWT login, bcrypt password hashing, two roles (ADMIN / USER). Only admins create users; only admins create/update/delete projects; users may only update the status of projects assigned to them. |
| Project CRUD | title, description, startDate, endDate, status (PENDING / IN_PROGRESS / COMPLETED), assignedUsers, attachments. |
| File upload | Multer (memory storage) → Cloudinary, max 3 attachments per project. |
| Dashboard analytics | Total users, total projects, project counts by status, projects ending within 7 days. |
| User module | Admin CRUD + role changes; every user can update their own profile (incl. avatar). |
| Bonus | Theme toggle, activity logs, search / filter / pagination, Vercel deployment. |
The brief suggests Axios + Redux/Context API on the frontend. This app instead uses Next.js Server Actions (App Router, React Server Components) for data fetching and mutations, which removes the need for a client-side data-fetching library and a global store. This is a deliberate, modern choice — the same requirements (API integration, role-aware UI, auth) are fully met.
.
├── frontend/ # Next.js 16 App Router app (React 19, Tailwind v4, shadcn/ui)
├── backend/ # Express 5 API (TypeScript, Prisma 6, MongoDB, Cloudinary)
│ └── src/docs/troology.postman_collection.json # Postman collection (also served live at /docs)
└── .env.example # reference for all environment variables
There is no root workspace config — frontend/ and backend/ are independent, each with its own package.json. Run commands from inside the relevant folder.
- Node.js 20+ (and optionally Bun — the frontend ships a
bun.lock) - A MongoDB instance with a replica set (Prisma requires it for writes; MongoDB Atlas works out of the box)
- A Cloudinary account (for file/avatar uploads)
Copy .env.example and split it into the two app-specific files (both are git-ignored):
- Backend →
backend/.env - Frontend →
frontend/.env.local
| Variable | App | Notes |
|---|---|---|
DATABASE_URL |
backend | MongoDB connection string (replica set required). Only required var — server throws on startup without it. |
PORT |
backend | Defaults to 4000. Ignored on Vercel serverless. |
NODE_ENV |
backend | development / production. |
CORS_ORIGINS |
backend | Comma-separated allowlist, no trailing slash. Defaults to localhost:3000 + deployed frontend. |
JWT_SECRET |
backend | Long random string. |
JWT_EXPIRES_IN |
backend | Defaults to 7d. |
CLOUDINARY_CLOUD_NAME / CLOUDINARY_API_KEY / CLOUDINARY_API_SECRET |
backend | From your Cloudinary dashboard. |
NEXT_PUBLIC_API_URL |
frontend | Base URL of the backend, no trailing slash (e.g. http://localhost:4000). |
cd backend
npm install # installs deps + runs `prisma generate` (postinstall)
npm run db:push # sync the Prisma schema to MongoDB
npm run db:seed # create the default admin (admin@troology.com / Admin@123)
npm run dev # tsx watch → http://localhost:4000Other backend scripts: npm run build/start, npm run typecheck, npm run db:studio (Prisma Studio), npm run db:generate.
cd frontend
bun install # or: npm install
bun run dev # → http://localhost:3000Other frontend scripts: bun run build, bun run start, bun run lint (Biome), bun run format.
Base URL: http://localhost:4000/api/v1 (or the deployed backend). All protected routes require an Authorization: Bearer <token> header obtained from POST /auth/login.
| Method | Path | Access | Body / Query |
|---|---|---|---|
GET |
/health |
public | — (DB ping) |
POST |
/auth/login |
public | { email, password } → { token, user } |
GET |
/auth/me |
any user | — |
GET |
/users |
admin | ?page&limit&search&role |
POST |
/users |
admin | multipart: name, email, password, role, optional avatar file |
GET |
/users/:id |
self or admin | — |
PATCH |
/users/:id |
admin | multipart: name, email, password?, role, optional avatar |
PATCH |
/users/me |
any user | multipart: name, email, password?, optional avatar |
DELETE |
/users/:id |
admin | — |
GET |
/projects |
any user | ?page&limit&search&status (non-admins see only their assigned projects) |
POST |
/projects |
admin | multipart: title, description, startDate, endDate, status?, assignedUserIds?, up to 3 attachments files |
GET |
/projects/:id |
admin or assignee | — |
PATCH |
/projects/:id |
admin | multipart (same fields as create; appends attachments) |
PATCH |
/projects/:id/status |
admin or assignee | { status } |
DELETE |
/projects/:id |
admin | — |
GET |
/dashboard |
admin | — (analytics) |
GET |
/activity |
admin | ?page&limit (activity log) |
assignedUserIds accepts a JSON array string, a comma-separated string, or repeated form fields.
- Browse: the backend serves an HTML API reference at
/docs, rendered from the Postman collection. - Import: point Postman at
https://troology-backend.vercel.app/docs/collection.json, or import the file directly frombackend/src/docs/troology.postman_collection.json.
The collection includes every endpoint, uses a {{baseUrl}} variable, and auto-captures the JWT from the login request into a {{token}} variable (via a test script), so every other request is pre-authorized.
Frontend — Next.js 16 (App Router, React 19 Server Components + Server Actions), TypeScript (strict), Tailwind CSS v4, shadcn/ui (radix + lucide), react-hook-form + zod, sonner, recharts, next-themes, @better-fetch/fetch, Biome (lint/format).
Backend — Node.js, Express 5, TypeScript (CommonJS), Prisma 6 ORM on MongoDB, JWT (jsonwebtoken), bcryptjs, Multer (memory storage), Cloudinary, tsx.
Both apps deploy as separate Vercel projects.
- Frontend — standard Next.js deployment; set
NEXT_PUBLIC_API_URLto the backend URL. - Backend — Express runs as a Vercel serverless function via
@vercel/node(backend/vercel.json). The app is CommonJS (Vercel's native ESM loader requires explicit.jsextensions, which the source doesn't use), exports the Expressappas the handler, and skips binding a port whenprocess.env.VERCELis set. Prisma'srhel-openssl-3.0.xbinary target is bundled for the serverless runtime. Set all backend env vars in the project's Settings → Environment Variables.