The high‑level architecture of the backend is shown below.
It illustrates the users flowing through an nginx load balancer and API gateway to the individual backend services, with PostgreSQL and Cloudinary for data and file storage.
Frontend repository (Next.js app): Proptech-frontend
This document explains how to:
- Run locally without Docker
- Run with Docker (dev & prod)
- Repopulate dev
node_modulesinside Docker when dependencies change
- Node.js 22+
- pnpm (via
corepack enableor installed globally) - Docker and Docker Compose (v2)
- A valid
.envfile inbackend/with at least:DATABASE_URLACCESS_SECRETREFRESH_SECRET
Optional env vars:
RATE_LIMIT_POINTS– API requests allowed per window (default: 300)RATE_LIMIT_DURATION– Window in seconds (default: 60). Increase limits if you hit 429 in Docker dev (shared IP).
The
.envfile is NOT copied into images and is read at runtime.
From backend/:
pnpm install
pnpm devApp starts on http://localhost:8000 using nodemon + tsx with hot reload.
For a local production-like run:
pnpm build
pnpm start # runs node dist/index.jsDev stack (from docker-compose.dev.yml):
appservice built fromDockerFile.dev(nodemon + tsx)nginxservice as reverse proxy on host port 8000- Code mounted from your machine for live reload
- Separate Docker volume for
node_modulesso Linux binaries are used
Note:
DockerFile.devrunspnpm install --frozen-lockfile && pnpm run devon container start.
Dependencies are installed inside the container automatically when you rundocker compose up.
From backend/:
docker compose -f docker-compose.dev.yml upThen open:
Behavior:
apprunspnpm run dev(nodemon + tsx)- Source code is live from your machine via
.:/app nginxlistens on host port 8000 and proxies toapp:8000
docker compose -f docker-compose.dev.yml up --build- Press Ctrl+C in the terminal where
upis running, then optionally:
docker compose -f docker-compose.dev.yml downProd stack (from docker-compose.prod.yml):
appservice built fromDockerFile- Installs dependencies
- Runs
pnpm run buildto producedist/ - Prunes dev dependencies
- Runs
node dist/index.js
nginxservice on host port 80, reverse-proxying toapp:8000
From backend/:
docker compose -f docker-compose.prod.yml up --buildThen open:
- http://localhost (host port 80 → nginx → app:8000)
docker compose -f docker-compose.prod.yml up --build -dTo stop:
docker compose -f docker-compose.prod.yml downTo populate the database with sample properties, tickets, units, and tenant assignments:
# From backend/, with API running at http://localhost:8000
./scripts/seed-dashboard.shRequires: Users must already exist (see § 5.1 below). The script expects:
anuj@gmail.com (Admin), manager@example.com, tech@example.com, tenant@example.com.
Requires: curl and node (Node.js comes with the project).
For Docker: ensure the API is reachable at http://localhost:8000 (or set API_URL).
End-to-end steps to test the app with sample data:
1. Start backend
cd backend && pnpm install && pnpm devAPI runs at http://localhost:8000.
2. Start frontend (new terminal)
cd frontend && pnpm install && pnpm devApp runs at http://localhost:3000.
3. Register test users (one-time, run from project root)
API="http://localhost:8000"
# Admin (use your own email or this one)
curl -sS -X POST "$API/api/v1/users/auth/register" \
-H "Content-Type: application/json" \
-d '{"name":"Admin","email":"anuj@gmail.com","password":"password123","role":"ADMIN"}'
# Manager, Technician, Tenant
curl -sS -X POST "$API/api/v1/users/auth/register" \
-H "Content-Type: application/json" \
-d '{"name":"Manager","email":"manager@example.com","password":"password123","role":"MANAGER"}'
curl -sS -X POST "$API/api/v1/users/auth/register" \
-H "Content-Type: application/json" \
-d '{"name":"Technician","email":"tech@example.com","password":"password123","role":"TECHNICIAN"}'
curl -sS -X POST "$API/api/v1/users/auth/register" \
-H "Content-Type: application/json" \
-d '{"name":"Tenant","email":"tenant@example.com","password":"password123","role":"TENANT"}'If a user already exists (e.g. email in use), that request will fail; the others can still succeed.
4. Run seed
cd backend && ./scripts/seed-dashboard.sh5. Log in and test
Go to http://localhost:3000/login and log in with:
| Role | Password | What to try | |
|---|---|---|---|
| Admin | anuj@gmail.com | password123 | Properties, Occupancy, assign managers, assign tenants to units |
| Manager | manager@example.com | password123 | Maintenance tickets, assign technicians, Occupancy, Properties |
| Technician | tech@example.com | password123 | Assigned tickets, update status (Start work, Mark done) |
| Tenant | tenant@example.com | password123 | Report Issue (create ticket with photos), view My tickets |
What the seed creates
- 2 properties (Sunset Apartments, Downtown Tower)
- Manager assigned to both properties
- 3 units (101, 102, 201) in Sunset Apartments
- Tenant assigned to unit 101 (so Report Issue and Occupancy work)
- 3 maintenance tickets (some assigned to technician)
- Notifications for manager and technician
- Local dev (no Docker): fastest feedback,
pnpm dev - Docker dev: test the app inside containers with nginx and live reload
- Docker prod: validate the production image + nginx configuration locally (same pattern you’d run in staging/production)
The workflow in .github/workflows/deploy.yml runs on push to main (or manually via “Run workflow”). It rsyncs the backend to EC2 and runs docker compose -f docker-compose.prod.yml up -d --build.
One-time setup on EC2
- Docker and Docker Compose installed
- Domain and Certbot configured (e.g.
/etc/letsencryptforprop-tech.live/api.prop-tech.live) - A deploy directory created, e.g.
mkdir -p /home/ec2-user/proptech-backend - A
.envfile in that directory (same variables as.env.example); the workflow does not overwrite.env
GitHub repository secrets (Settings → Secrets and variables → Actions):
| Secret | Example | Description |
|---|---|---|
EC2_HOST |
api.prop-tech.live or EC2 public IP |
SSH host |
EC2_USER |
ec2-user (Amazon Linux) or ubuntu |
SSH user |
EC2_SSH_PRIVATE_KEY |
Contents of your .pem key |
Private key for SSH |
EC2_DEPLOY_PATH |
/home/ec2-user/proptech-backend |
Absolute path to the app on EC2 |
First run: ensure .env exists at EC2_DEPLOY_PATH on the server before triggering the workflow.
