A full-stack rental property discovery platform connecting tenants with verified listings and property managers with streamlined tenant management.
┌─────────────────────────────────────────────────────────────┐
│ User │
└──────────────────────────┬──────────────────────────────────┘
│
┌────────────▼────────────┐
│ Next.js 15 (React 19) │
│ Mapbox GL · Redux │
└────────────┬────────────┘
│ REST API
┌────────────▼────────────┐
│ Express 5 (Node.js) │
│ AWS EC2 │
│ JWT Auth · Multer │
└──┬─────────┬─────────┬──┘
│ │ │
┌───────────▼──┐ ┌───▼───┐ ┌───▼──────────┐
│ PostgreSQL │ │ AWS │ │ Nominatim │
│ + PostGIS │ │ S3 │ │ (Geocoding) │
│ Amazon RDS │ │ │ │ │
└──────────────┘ └───────┘ └──────────────┘
Property Search — Filter by location (50km radius via PostGIS), price, beds, baths, square feet, property type, amenities, and availability
Interactive Map — Mapbox GL with property markers, popups, and fly-to animations
Grid / List Toggle — Switch between card and compact list views
Favorites — Save and manage favorite properties
Applications — Submit rental applications with contact details
Residences — View active leases with property details and billing history
Settings — Update profile information
Property Listing — Create properties with drag-and-drop photo uploads (S3), amenities, highlights, and auto-geocoded addresses
Application Management — Review, approve, or deny applications with tabbed filtering (All / Pending / Approved / Denied)
Tenant Overview — Per-property tenant list with lease periods, rent, and payment status
Auto Lease Creation — Approving an application automatically creates a 1-year lease record
Settings — Update manager profile
Role-Based Access — Separate dashboards, API routes, and middleware for tenants and managers
Responsive Design — Mobile-first with breakpoints at 375px, 640px, 768px, 1024px, 1280px
Mobile Navigation — Hamburger menu with full navigation drawer
Toast Notifications — Success/error feedback on all mutations via Sonner
Image Fallbacks — Placeholder images on load failure
Legal Pages — Terms, privacy, cookies, FAQ
Contact Form — Public contact page
Technology
Purpose
Next.js 15
React framework with App Router
React 19
UI library
TypeScript 5
Type safety
Redux Toolkit + RTK Query
State management and API caching
Tailwind CSS 4
Utility-first styling
Radix UI (shadcn/ui)
Accessible component primitives
Mapbox GL
Interactive map rendering
AWS Amplify
Authentication (Cognito)
Framer Motion
Landing page animations
React Hook Form + Zod
Form handling and validation
FilePond
Drag-and-drop image uploads
Sonner
Toast notifications
Technology
Purpose
Express 5
HTTP server
TypeScript 5
Type safety
Prisma 6
ORM with PostgreSQL
PostGIS
Geospatial queries (radius search)
AWS Cognito
JWT-based authentication
AWS S3
Property image storage
Multer
Multipart file upload handling
Nominatim
Address geocoding (OpenStreetMap)
Helmet
Security headers
Morgan
HTTP request logging
k6
Load and performance testing
Service
Purpose
AWS Amplify
Frontend hosting + CI/CD
AWS EC2
Backend API hosting
Amazon RDS
PostgreSQL + PostGIS database
AWS S3
Image object storage
AWS Cognito
User pool and authentication
PM2
Production process management
7 models with PostGIS spatial support:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Manager │────►│ Property │◄────│ Location │
│ │ 1:N │ │ 1:1 │ (PostGIS)│
└──────────┘ └────┬─────┘ └──────────┘
│
┌───────┼───────┐
│ │ │
┌────▼──┐ ┌──▼───┐ ┌─▼────────────┐
│ Lease │ │Tenant│ │ Application │
│ │ │ │ │ │
└───┬───┘ └──────┘ └──────────────┘
│
┌────▼───┐
│Payment │
└────────┘
Tenant ◄──► Property (M:N — favorites)
Tenant ◄──► Property (M:N — current residences)
Model
Key Fields
Property
name, description, pricePerMonth, beds, baths, squareFeet, propertyType, amenities[], highlights[], photoUrls[]
Manager
cognitoId, name, email, phoneNumber
Tenant
cognitoId, name, email, phoneNumber, favorites[], properties[]
Location
address, city, state, country, postalCode, coordinates (geography Point 4326)
Application
status (Pending/Approved/Denied), propertyId, tenantCognitoId, name, email, message
Lease
startDate, endDate, rent, deposit, propertyId, tenantCognitoId
Payment
amountDue, amountPaid, dueDate, paymentDate, paymentStatus
habiful/
├── client/
│ ├── public/
│ ├── src/
│ │ ├── app/
│ │ │ ├── (auth)/
│ │ │ ├── (dashboard)/
│ │ │ │ ├── managers/
│ │ │ │ └── tenants/
│ │ │ ├── (nondashboard)/
│ │ │ │ ├── landing/
│ │ │ │ └── search/
│ │ │ ├── (legal)/
│ │ │ ├── about/
│ │ │ ├── contact/
│ │ │ ├── signin/
│ │ │ └── signup/
│ │ ├── components/
│ │ │ ├── ui/
│ │ │ ├── Card.tsx
│ │ │ ├── CardCompact.tsx
│ │ │ ├── ApplicationCard.tsx
│ │ │ ├── Navbar.tsx
│ │ │ ├── AppSidebar.tsx
│ │ │ └── ...
│ │ ├── hooks/
│ │ ├── lib/
│ │ ├── state/
│ │ └── types/
│ └── package.json
│
├── server/
│ ├── prisma/
│ │ ├── schema.prisma
│ │ ├── seed.ts
│ │ ├── seedData/
│ │ └── migrations/
│ ├── src/
│ │ ├── controllers/
│ │ ├── middlewares/
│ │ ├── routes/
│ │ ├── utils/
│ │ └── index.ts
│ ├── tests/k6/
│ ├── ecosystem.config.js
│ └── package.json
│
└── README.md
Node.js 18+
PostgreSQL 14+ with PostGIS extension
AWS account (Cognito + S3)
Mapbox account (access token)
git clone < repository-url>
cd habiful
cd server && npm install
cd ../client && npm install
psql -U postgres -c " CREATE DATABASE \" realEstate\" ;"
psql -U postgres -d realEstate -c " CREATE EXTENSION IF NOT EXISTS postgis;"
cd server
npx prisma migrate dev
npm run seed
server/.env
PORT = 8000
DATABASE_URL = " postgresql://postgres:postgres@localhost:5433/realEstate?schema=public"
AWS_REGION = us-east-1
S3_BUCKET_NAME = your-s3-bucket
AWS_ACCESS_KEY_ID = your-access-key
AWS_SECRET_ACCESS_KEY = your-secret-key
client/.env
NEXT_PUBLIC_API_BASE_URL = http://localhost:8000
NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN = your-mapbox-token
NEXT_PUBLIC_AWS_COGNITO_USER_POOL_ID = your-pool-id
NEXT_PUBLIC_AWS_COGNITO_USER_POOL_CLIENT_ID = your-client-id
4. Generate Prisma Client
cd server
npm run prisma:generate
5. Run Development Servers
# Terminal 1 — Server (http://localhost:8000)
cd server && npm run dev
# Terminal 2 — Client (http://localhost:3000)
cd client && npm run dev
Method
Endpoint
Description
GET
/
Health check
GET
/properties
List properties with filters
GET
/properties/:id
Get property with coordinates
GET
/properties/:id/leases
Get property leases
Tenant Endpoints (Auth Required)
Method
Endpoint
Description
GET
/tenants/:cognitoId
Get tenant with favorites
PUT
/tenants/:cognitoId
Update settings
POST
/tenants
Create tenant
GET
/tenants/:cognitoId/current-residences
Active residences
POST
/tenants/:cognitoId/favorites/:propertyId
Add favorite
DELETE
/tenants/:cognitoId/favorites/:propertyId
Remove favorite
Manager Endpoints (Auth Required)
Method
Endpoint
Description
GET
/managers/:cognitoId
Get manager
PUT
/managers/:cognitoId
Update settings
POST
/managers
Create manager
GET
/managers/:cognitoId/properties
Managed properties
POST
/properties
Create property (multipart)
Application Endpoints (Auth Required)
Method
Endpoint
Description
GET
/applications
List (with userId + userType filters)
POST
/applications
Submit (tenant only)
PUT
/applications/:id/status
Approve/deny (manager only)
Lease Endpoints (Auth Required)
Method
Endpoint
Description
GET
/leases
List all leases
GET
/leases/:id/payments
Payments for a lease
Parameter
Type
Description
priceMin / priceMax
number
Monthly rent range
beds / baths
number or "any"
Minimum count
propertyType
enum or "any"
Rooms, Tinyhouse, Apartment, Villa, Townhouse, Cottage
squareFeetMin / squareFeetMax
number
Area range
amenities
string
Comma-separated (Pool,Gym,WiFi)
availableFrom
date
No active lease after this date
latitude / longitude
number
Center for 50km radius search (PostGIS)
favoriteIds
string
Comma-separated property IDs
During deployment, image uploads worked correctly in local development but resulted in corrupted files in the production environment. The issue was traced to differences in request handling between local and cloud setups, specifically related to multipart data processing.
Resolving this required understanding how request streams behave in production, correcting middleware order and binary handling, and verifying uploads end-to-end rather than relying on local success. This reinforced the importance of production-aware debugging and careful handling of file uploads in cloud-hosted applications.
cd server
npm run k6:smoke # 1 VU, 1 min — quick sanity check
npm run k6:load # Ramp to 10 VUs, 9 min — normal traffic
npm run k6:stress # Ramp to 100 VUs, 12 min — find breaking point
npm run k6:spike # Burst to 100 VUs, 4 min — sudden traffic
npm run k6:tenant # Tenant endpoints
npm run k6:manager # Manager endpoints
npm run k6:applications # Application CRUD
npm run k6:leases # Lease + payments
Tests are in server/tests/k6/ and require valid JWT tokens in .env.
Layer
Service
Details
Frontend
AWS Amplify
Auto-deploys from Git, serves Next.js
Backend
AWS EC2
PM2 process manager, Express API
Database
Amazon RDS
PostgreSQL 14 + PostGIS
Storage
AWS S3
Property images
Auth
AWS Cognito
User pool with custom role attribute
# Server production build
cd server && npm run build
pm2 start ecosystem.config.js
# Client production build
cd client && npm run build
Script
Description
npm run dev
Dev server with hot reload
npm run build
Compile to dist/
npm run start
Build + start production
npm run seed
Seed database
npm run prisma:generate
Generate Prisma client + copy types to client
Script
Description
npm run dev
Next.js dev server
npm run build
Production build
npm run start
Start production
npm run lint
ESLint
ISC