Click to expand full navigation
- 💡 Project Overview
- 🌐 Live Demo
- 📸 Screenshots
- ✨ Features
- 🛠 Tech Stack
- 🏗 System Architecture
- 🗄 Database Schema
- 📂 Project Structure
- 💻 Prerequisites
- 🚀 Quick Start
- 🔑 Environment Variables
- 📚 API Reference
- 🔐 Authentication & Security
- 📱 Responsive Design
- ⚡ Performance & Optimization
- ☁️ Deployment
- 🗺️ Roadmap
- 🤝 Contributing
- ❓ FAQ
- 📄 License
- 👨💻 Developer
RAJSPACE is a full-stack, production-deployed rental marketplace inspired by Airbnb — engineered from the ground up to demonstrate scalable MVC architecture, secure authentication pipelines, and real-world API integrations.
Built entirely with Node.js + Express + MongoDB, RAJSPACE enables users to discover unique stays across categories, list their own properties with geo-tagged map pins, review experiences with star ratings, and curate a personal wishlist — all within a polished, mobile-responsive UI.
This is not a tutorial clone. Every layer — from Joi schema validation and Passport.js session management to Cloudinary CDN image delivery and Mapbox geocoding — reflects production engineering standards. It is deployed live on Render with a MongoDB Atlas database and a secure, encrypted session store.
| Property | Value |
|---|---|
| Type | Full-Stack Web Application (SaaS-style rental platform) |
| Architecture | Strict MVC (Model · View · Controller) |
| Rendering | Server-Side Rendering (EJS + ejs-mate layout engine) |
| Auth | Session-Based · Passport.js Local Strategy · PBKDF2 Hashing |
| Database | MongoDB Atlas · Mongoose ODM · Persistent MongoStore Sessions |
| Image Delivery | Cloudinary CDN with multer-storage-cloudinary |
| Maps & Geocoding | Mapbox GL JS + Mapbox Geocoding API |
| Validation | Dual-layer · Joi (server) + Bootstrap (client) |
| Deployment | Render (PaaS) · Node.js 22.x runtime |
| Environment | URL | Status |
|---|---|---|
| 🟢 Production | https://rajspace.onrender.com | Live |
| 💻 Local | http://localhost:8080 |
Dev Only |
Seed Credentials — After running
npm run seed: Username:rajspace-seed· Password:seed-pass-123
Home · Explore all stays with category-based filtering and live search |
Category Filter · Scroll through Trending, Beach, Mountain, Luxury & more |
Listing Detail · Owner info, pricing, booking button |
Interactive Mapbox Map · Star Ratings · Guest Reviews |
Login · Secure session auth with redirect preservation |
Create Listing · Multi-field form with Cloudinary image upload |
| Feature | Implementation |
|---|---|
| User Registration | POST /signup · passport-local-mongoose handles salting + PBKDF2 hashing |
| Secure Login | POST /login · Passport Local Strategy verifies credentials |
| Session Persistence | connect-mongo stores sessions in MongoDB Atlas; touchAfter: 24h reduces writes |
| Post-Auth Redirect | saveRedirectUrl middleware restores the user's original destination after login |
| Graceful Logout | GET /logout · req.logout() clears session; flash confirms logout |
| Feature | Implementation |
|---|---|
| Browse All Listings | GET /listings · Supports ?category= and ?q= query params |
| Full-Text Search | MongoDB $regex across title, location, country, and category fields |
| Category Filtering | 11 enum categories: Trending, Rooms, Entire Home, Pool, Beach, Mountain, Nature, City, Luxury, Family, Pet Friendly |
| View Listing Detail | GET /listings/:id · Nested .populate() for owner + reviews + review authors |
| Create Listing | POST /listings · Cloudinary image upload + Mapbox geocoding on save |
| Edit Listing | PUT /listings/:id · Re-geocodes location if changed; replaces image if new file uploaded |
| Delete Listing | DELETE /listings/:id · Mongoose post-hook cascades deletion of all linked reviews |
| Ownership Guard | isOwner middleware prevents unauthorized edit/delete at route level |
| Feature | Implementation |
|---|---|
| Forward Geocoding | Mapbox Geocoding API converts location string → GeoJSON Point coordinates on create/edit |
| Interactive Map | Mapbox GL JS renders a dynamic, zoomable pin at listing's geocoded coordinates |
| Graceful Fallback | If Mapbox is unavailable, coordinates default to [72.88, 19.08] (Mumbai) |
| Null-Safe Owner | Orphaned listings display "RAJSPACE Host" as a fallback owner label |
| Feature | Implementation |
|---|---|
| Create Review | POST /listings/:id/reviews · Star rating (1–5) + written comment stored in Review model |
| Delete Review | DELETE /listings/:id/reviews/:reviewId · Author-only guard via isReviewAuthor middleware |
| Review Analytics | Controller computes average rating and review count dynamically before rendering |
| Cascade Deletion | Mongoose findOneAndDelete post-hook removes all reviews when a listing is deleted |
| Feature | Implementation |
|---|---|
| Add to Wishlist | POST /wishlist/:id · Stores listing ObjectId in req.session.wishlist array |
| Remove from Wishlist | DELETE /wishlist/:id · Filters ID out of session array |
| View Wishlist | GET /wishlist · Queries MongoDB for all saved IDs (max 50) and renders the grid |
| Duplicate Guard | Checks for existing ID before pushing to prevent duplicate entries |
| Feature | Implementation |
|---|---|
| Direct-to-Cloud Uploads | multer + multer-storage-cloudinary streams uploads directly to rajspace_dev folder |
| Accepted Formats | PNG, JPG, JPEG only (whitelisted in CloudinaryStorage config) |
| Image Transform | URL rewritten with /upload/w_250 in edit preview for optimized thumbnail |
| Graceful Fallback | If Cloudinary is unconfigured, uses a high-quality Unsplash placeholder image |
| Feature | Details |
|---|---|
| Custom Teal Theme | Bootstrap 5 overridden with a curated teal/dark palette via style.css |
| Dark Mode Support | Optimized dark-mode compatibility for system-level preference |
| Scrollable Category Chips | Horizontally scrollable filter bar inspired by Airbnb's design language |
| Tax Price Toggle | JavaScript toggle shows/hides price-with-tax display on listing cards |
| Star Rating Component | Dedicated rating.css renders semantic, interactive star ratings |
| Flash Notifications | connect-flash surfaces success/error banners after every action |
| Verified Host Badge | Visual badge displayed on owner's listings |
| Legal Pages | /privacy-policy and /terms-of-service static routes via legal.js router |
🔍 View complete dependency breakdown
| Technology | Version | Role |
|---|---|---|
| Node.js | 22.x |
JavaScript runtime · Powers the server |
| Express.js | ^5.2.1 |
Web framework · Routing · Middleware pipeline |
| Mongoose | ^9.1.3 |
MongoDB ODM · Schema definitions · Query building |
| MongoDB | Atlas (cloud) | NoSQL document database · Persistent data store |
| Passport.js | ^0.7.0 |
Authentication middleware framework |
| passport-local | ^1.0.0 |
Username/password authentication strategy |
| passport-local-mongoose | ^9.0.1 |
Mongoose plugin · PBKDF2 hashing · Auto user methods |
| express-session | ^1.18.2 |
Session management middleware |
| connect-mongo | ^6.0.0 |
MongoDB-backed session store with crypto |
| Joi | ^18.0.2 |
Declarative schema validation for all incoming data |
| connect-flash | ^0.1.1 |
Flash message middleware for user feedback |
| method-override | ^3.0.0 |
Enables PUT/DELETE via HTML forms |
| dotenv | ^17.2.3 |
Environment variable management |
| Technology | Version | Role |
|---|---|---|
| EJS | ^3.1.10 |
Embedded JavaScript templating engine |
| ejs-mate | ^4.0.0 |
Layout/partial engine for DRY EJS templates |
| Bootstrap 5 | 5.3.x (CDN) |
Responsive grid · UI components · Utility classes |
| Font Awesome | CDN | Icon library for UI elements |
| Mapbox GL JS | CDN | Interactive WebGL-powered maps |
| Vanilla CSS/JS | Custom | style.css, rating.css, script.js, map.js |
| Technology | Role |
|---|---|
| Cloudinary | Image CDN · Upload storage · On-the-fly transformations |
| Mapbox Geocoding API | Converts location text → GeoJSON coordinates |
| MongoDB Atlas | Cloud-hosted database with connection pooling |
| Render (PaaS) | Production hosting · Node.js 22 runtime · Auto-deploy |
| Tool | Role |
|---|---|
| multer | ^2.0.2 — Multipart form data parsing for image uploads |
| multer-storage-cloudinary | ^4.0.0 — Cloudinary storage engine for multer |
@mapbox/mapbox-sdk |
^0.16.2 — Official Mapbox Node.js SDK for geocoding |
flowchart TD
Browser(["🌐 Browser / Client"])
subgraph ExpressServer["⚡ Express Server — Node.js 22"]
direction TB
Router["🔀 Express Router\n(listing · review · user · wishlist · legal)"]
Middleware["🛡 Middleware Pipeline\nisLoggedIn · isOwner · isReviewAuthor\nvalidateListing · validateReview · saveRedirectUrl\nPassport Session · Flash · method-override"]
Controller["🎮 Controllers\nlistings.js · reviews.js · users.js"]
Utils["🔧 Utilities\nwrapAsync · ExpressError"]
end
subgraph DataLayer["🗄 Data Layer"]
MongoDB[("📦 MongoDB Atlas\nListings · Reviews · Users\nSession Store")]
Cloudinary(["🖼 Cloudinary CDN\nImage Storage"])
Mapbox(["🗺 Mapbox API\nGeocoding Service"])
end
subgraph Views["📄 EJS Views (SSR)"]
Layout["boilerplate.ejs layout"]
Templates["listings · users · wishlist · legal · error"]
end
Browser -- "HTTP Request" --> Router
Router --> Middleware
Middleware --> Controller
Controller <-- "Mongoose ODM" --> MongoDB
Controller -- "multer upload" --> Cloudinary
Controller -- "forwardGeocode()" --> Mapbox
Controller -- "res.render()" --> Views
Views -- "HTML Response" --> Browser
Utils -- "error handling" --> Controller
sequenceDiagram
autonumber
actor User
participant Browser
participant Express
participant Passport
participant MongoDB
User->>Browser: POST /login (username + password)
Browser->>Express: HTTP POST
Express->>Passport: passport.authenticate('local')
Passport->>MongoDB: Find user by username
MongoDB-->>Passport: User document (hash + salt)
Passport->>Passport: PBKDF2 verify(password, hash, salt)
alt ✅ Valid credentials
Passport->>Express: req.user populated
Express->>MongoDB: Serialize user → session store
Express-->>Browser: Set-Cookie (session ID) + Redirect
Browser-->>User: Welcome flash + Dashboard
else ❌ Invalid credentials
Passport-->>Express: auth failure
Express-->>Browser: Flash error + Redirect /login
Browser-->>User: "Invalid username or password"
end
sequenceDiagram
autonumber
actor User
participant Router
participant isLoggedIn
participant isOwner
participant validateListing
participant Controller
participant Model
participant DB
User->>Router: PUT /listings/:id
Router->>isLoggedIn: Check req.isAuthenticated()
alt Not authenticated
isLoggedIn-->>User: Save URL → Flash → Redirect /login
end
isLoggedIn->>isOwner: Check listing.owner === currUser._id
alt Not owner
isOwner-->>User: Flash error → Redirect /listings/:id
end
isOwner->>validateListing: Joi schema validation
alt Invalid payload
validateListing-->>User: 400 ExpressError
end
validateListing->>Controller: updateListing()
Controller->>Model: Listing.findById() + geocode + save()
Model->>DB: MongoDB write
DB-->>Model: Updated document
Model-->>Controller: listing
Controller-->>User: Flash success + Redirect
graph TB
subgraph Routes["🔀 Routes Layer"]
R1[listing.js]
R2[review.js]
R3[user.js]
R4[wishlist.js]
R5[legal.js]
end
subgraph Middleware["🛡 Middleware"]
M1[isLoggedIn]
M2[isOwner]
M3[isReviewAuthor]
M4[validateListing]
M5[validateReview]
M6[saveRedirectUrl]
end
subgraph Controllers["🎮 Controllers"]
C1[listings.js]
C2[reviews.js]
C3[users.js]
end
subgraph Models["📦 Models"]
Mo1[Listing]
Mo2[Review]
Mo3[User]
end
subgraph Views["📄 EJS Views"]
V1[listings/]
V2[users/]
V3[wishlist/]
V4[legal/]
V5[error.ejs]
end
subgraph Services["☁️ External Services"]
S1[Cloudinary CDN]
S2[Mapbox Geocoding]
S3[MongoDB Atlas]
end
R1 --> M1 & M2 & M4
R2 --> M1 & M3 & M5
R3 --> M6
M1 & M2 & M3 & M4 & M5 --> C1 & C2 & C3
C1 --> Mo1 & Mo2
C2 --> Mo2 & Mo1
C3 --> Mo3
Mo1 --> S3
Mo2 --> S3
Mo3 --> S3
C1 --> S1 & S2
C1 --> V1
C2 --> V1
C3 --> V2
erDiagram
USER ||--o{ LISTING : "owns (1-to-many)"
USER ||--o{ REVIEW : "writes (1-to-many)"
LISTING ||--o{ REVIEW : "has (1-to-many)"
USER {
ObjectId _id PK
string username "unique, indexed by passport-local-mongoose"
string email "required"
string hash "PBKDF2 hashed password"
string salt "crypto salt"
}
LISTING {
ObjectId _id PK
string title "required"
string description
object image "url + filename"
number price
string location
string country
string category "enum — 11 types"
object geometry "GeoJSON Point — coordinates[lng, lat]"
ObjectId owner FK "ref: User"
ObjectId[] reviews FK "ref: Review[]"
}
REVIEW {
ObjectId _id PK
string comment "required"
number rating "1–5, required"
Date createdAt "auto-set to Date.now()"
ObjectId author FK "ref: User"
}
All 11 supported property categories:
| Icon | Category | Icon | Category |
|---|---|---|---|
| 🔥 | Trending | 🏖 | Beach |
| 🛏 | Rooms | ⛰ | Mountain |
| 🏠 | Entire Home | 🌿 | Nature |
| 🏊 | Pool | 🌆 | City |
| 💎 | Luxury | 👨👩👧 | Family |
| 🐾 | Pet Friendly |
rajspace/
│
├── 📁 assets/ # Branding & banner assets
│ └── banner.png
│
├── 📁 screenshots/ # README documentation screenshots
│ ├── home_v3.png
│ ├── category_v3.png
│ ├── listinginfo_v3.png
│ ├── mapandreviews_v3.png
│ ├── create_v3.png
│ └── login_v3.png
│
├── 📁 controllers/ # 🎮 Business Logic Layer (MVC — Controller)
│ ├── listings.js # CRUD + geocoding + review analytics
│ ├── reviews.js # Create & cascade-delete reviews
│ └── users.js # Register · Login · Logout lifecycle
│
├── 📁 models/ # 📦 Data Layer (MVC — Model)
│ ├── listing.js # Listing schema · GeoJSON · post-delete hook
│ ├── review.js # Review schema · rating 1–5 · author ref
│ └── user.js # User schema · passport-local-mongoose plugin
│
├── 📁 routes/ # 🔀 Routing Layer
│ ├── listing.js # RESTful CRUD for /listings
│ ├── review.js # /listings/:id/reviews endpoints
│ ├── user.js # /signup · /login · /logout
│ ├── wishlist.js # /wishlist GET · POST · DELETE
│ └── legal.js # /privacy-policy · /terms-of-service
│
├── 📁 views/ # 📄 Presentation Layer (MVC — View)
│ ├── layouts/
│ │ └── boilerplate.ejs # Base HTML shell (navbar + footer + flash)
│ ├── includes/
│ │ ├── navbar.ejs # Responsive navigation bar
│ │ ├── footer.ejs # Footer with legal links
│ │ └── flash.ejs # Success / error notification banners
│ ├── listings/
│ │ ├── index.ejs # Explore · Category filter · Search
│ │ ├── show.ejs # Detail · Map · Reviews · Wishlist button
│ │ ├── new.ejs # Create listing form
│ │ ├── edit.ejs # Edit listing form (image preview)
│ │ └── reserve.ejs # Reservation confirmation page
│ ├── users/
│ │ ├── login.ejs # Login form
│ │ └── signup.ejs # Registration form
│ ├── wishlist/
│ │ └── index.ejs # Saved listings grid
│ ├── legal/
│ │ ├── privacy-policy.ejs # Privacy Policy static page
│ │ └── terms-of-service.ejs # Terms of Service static page
│ └── error.ejs # Unified 404 / 500 error boundary
│
├── 📁 public/ # Static assets (served by express.static)
│ ├── css/
│ │ ├── style.css # Custom teal theme + Bootstrap overrides
│ │ └── rating.css # Starability — pure CSS star rating component
│ └── js/
│ ├── script.js # Client-side: tax toggle, form validation
│ └── map.js # Mapbox GL JS map initialization + marker
│
├── 📁 utils/ # 🔧 Utility Helpers
│ ├── ExpressError.js # Custom error class (statusCode + message)
│ └── wrapAsync.js # Async route wrapper (eliminates try/catch boilerplate)
│
├── 📁 init/ # 🌱 Database Seed Scripts
│ ├── index.js # Seed runner (creates user + sample listings)
│ └── data.js # Sample listing payload array
│
├── app.js # 🚀 Application entry point
│ # Express config · Middleware stack · Route mounting
│ # Passport init · Session config · Error handlers
│
├── cloudConfig.js # ☁️ Cloudinary + multer-storage-cloudinary setup
│ # Graceful fallback to memoryStorage if unconfigured
│
├── middleware.js # 🛡 All custom middleware exports
│ # isLoggedIn · isOwner · isReviewAuthor
│ # validateListing · validateReview · saveRedirectUrl
│
├── schema.js # ✅ Joi validation schemas (listing + review)
├── package.json # Project metadata + scripts + dependencies
├── .env.example # Environment variable template
├── .env # 🔒 Secrets (gitignored)
└── README.md # This document
Ensure your environment meets these requirements before setup:
| Requirement | Version | Verify |
|---|---|---|
| Node.js | v22.x (LTS) |
node -v |
| npm | v10.x+ |
npm -v |
| MongoDB | v7.x+ (Local) or Atlas |
mongod --version |
| Git | Any recent version | git --version |
You will also need free accounts on:
- Cloudinary — for image hosting
- Mapbox — for maps and geocoding
- MongoDB Atlas — for cloud database (optional for local dev)
git clone https://github.com/rajayush6200/rajspace.git
cd rajspacenpm installcp .env.example .envOpen .env and populate all required values (see Environment Variables section below).
npm run seedThis creates a demo user
rajspace-seed(password:seed-pass-123) and seeds a collection of sample property listings.
npm startOpen your browser and navigate to http://localhost:8080
The root
/route automatically redirects to/listings— you'll land directly on the explore page.
Create a .env file in the project root with the following variables:
# ─── Database ──────────────────────────────────────────────
ATLASDB_URL=mongodb://127.0.0.1:27017/rajspace
# For MongoDB Atlas: mongodb+srv://<user>:<password>@cluster.mongodb.net/rajspace
# ─── Session Security ──────────────────────────────────────
SECRET=your-super-secret-random-string-minimum-32-chars
# ─── Mapbox (Maps & Geocoding) ─────────────────────────────
MAP_TOKEN=pk.eyJ1IjoieW91ci11c2VybmFtZSIsImEiOiJ...
# ─── Cloudinary (Image Hosting) ────────────────────────────
CLOUD_NAME=your-cloudinary-cloud-name
CLOUD_API_KEY=123456789012345
CLOUD_API_SECRET=your-cloudinary-api-secret
# ─── Runtime ───────────────────────────────────────────────
NODE_ENV=development
# Set to 'production' on live deployment| Variable | Required | Description |
|---|---|---|
ATLASDB_URL |
✅ | MongoDB connection string (local or Atlas) |
SECRET |
✅ | Session encryption secret — use a long, random string |
MAP_TOKEN |
✅ | Mapbox public token for GL JS and Geocoding API |
CLOUD_NAME |
✅ | Cloudinary cloud identifier |
CLOUD_API_KEY |
✅ | Cloudinary API key |
CLOUD_API_SECRET |
✅ | Cloudinary API secret |
NODE_ENV |
❌ | development (default) or production |
Note: If
CLOUD_NAME,CLOUD_API_KEY, orCLOUD_API_SECRETare not set, the app gracefully falls back tomulter.memoryStorage()and uses an Unsplash placeholder for listing images.
| Method | Route | Description |
|---|---|---|
GET |
/ |
Redirects to /listings |
GET |
/listings |
Explore all listings. Supports ?category=Beach and ?q=search+term |
GET |
/listings/:id |
Full listing detail with owner, reviews, analytics & map |
GET |
/privacy-policy |
Privacy Policy page |
GET |
/terms-of-service |
Terms of Service page |
| Method | Route | Middleware | Description |
|---|---|---|---|
GET |
/signup |
— | Render signup form |
POST |
/signup |
— | Register user, auto-login, redirect to /listings |
GET |
/login |
— | Render login form |
POST |
/login |
passport.authenticate('local') |
Login, restore redirect URL |
GET |
/logout |
— | Destroy session, redirect to /listings |
| Method | Route | Middleware | Description |
|---|---|---|---|
GET |
/listings/new |
isLoggedIn |
Render create listing form |
POST |
/listings |
isLoggedIn · upload · validateListing |
Create listing with geocoding |
GET |
/listings/:id/edit |
isLoggedIn |
Render edit form |
PUT |
/listings/:id |
isLoggedIn · isOwner · upload · validateListing |
Update listing, re-geocode |
DELETE |
/listings/:id |
isLoggedIn · isOwner |
Delete listing + cascade reviews |
POST |
/listings/:id/reviews |
isLoggedIn · validateReview |
Post new review |
DELETE |
/listings/:id/reviews/:reviewId |
isLoggedIn · isReviewAuthor |
Delete own review |
GET |
/wishlist |
— | View saved listings (session-based) |
POST |
/wishlist/:id |
— | Add listing to wishlist |
DELETE |
/wishlist/:id |
— | Remove listing from wishlist |
GET |
/listings/:id/reserve |
— | View reservation confirmation page |
RAJSPACE implements a multi-layer, session-based authentication system:
User credentials (username + password)
│
▼
passport.authenticate('local')
│
▼
passport-local-mongoose
verifies password against
PBKDF2 hash stored in MongoDB
│
┌────┴────┐
│ │
✅ Valid ❌ Invalid
│ │
▼ ▼
req.login() Flash error
Serialize Redirect /login
user ID →
MongoDB
session store
│
▼
Set-Cookie (session ID, httpOnly)
Redirect to saved URL or /listings
| Layer | Implementation |
|---|---|
| Password Hashing | passport-local-mongoose uses PBKDF2 (Node.js crypto) with unique salt per user |
| Session Encryption | connect-mongo stores sessions with crypto.secret encryption in MongoDB |
| Cookie Hardening | httpOnly: true · expires set to 7 days · No secure flag in dev (enabled in prod) |
| Route Authorization | isOwner and isReviewAuthor middleware checks identity at DB level, not just session |
| Payload Validation | Joi schemas reject malformed, missing, or out-of-range data server-side before controller runs |
| XSS Prevention | EJS escapes all template variables by default (<%= %> not <%- %>) |
| Redirect Poisoning | saveRedirectUrl stores only req.originalUrl from app-controlled routing |
| Env Secrets | .env is gitignored; no secrets ever hardcoded in source code |
RAJSPACE is built mobile-first using Bootstrap 5's grid system with custom CSS overrides in style.css. Key responsive behaviors:
| Breakpoint | Layout Behavior |
|---|---|
< 576px (xs) |
Single-column listing cards · Collapsed navigation · Full-width forms |
576px–768px (sm) |
2-column card grid · Expanded navigation |
768px–992px (md) |
3-column card grid · Side-by-side form fields |
> 992px (lg+) |
4-column card grid · Full sidebar + map layout |
Additional responsive features:
- Horizontally scrollable category chip bar (no wrapping on mobile)
- Fluid images via
width: 100%on all listing images - Responsive map container that scales with viewport
- Stacked review cards on narrow screens
| Optimization | Implementation |
|---|---|
| Session Write Reduction | connect-mongo configured with touchAfter: 24 * 3600 — only updates session timestamp once per day |
| Selective Populate | .populate('reviews').populate('owner') used only on listing detail; index page does not over-fetch |
| Async Error Elimination | wrapAsync wraps every async route handler, removing repetitive try/catch boilerplate throughout |
| Cloudinary CDN Delivery | All images served from Cloudinary's globally distributed CDN with automatic format optimization |
| Image Resize in Edit | URL rewritten with /upload/w_250 query for the edit form preview — avoids loading full-resolution image |
| Geocoding Fallback | Synchronous default coordinates used if Mapbox fails, preventing request timeouts |
| Static Asset Serving | express.static serves CSS/JS from /public with Express's built-in ETag/caching headers |
| DNS Resolution | dns.setDefaultResultOrder('ipv4first') at startup prevents IPv6 connection delays on Render/Atlas |
RAJSPACE is deployed to Render as a Node.js Web Service connected to MongoDB Atlas.
-
NODE_ENV=productionset in Render environment - MongoDB Atlas cluster configured with network access rules
- All 6 environment variables set in Render's environment panel
-
engines.node: "22.20.0"declared inpackage.jsonfor runtime pinning -
npm startconfigured as the start command - HTTPS enforced by default on Render's free subdomain
| Platform | Notes |
|---|---|
| Render | ✅ Currently deployed here — free tier, native Atlas integration |
| Railway | Superior env var management, fast deploys |
| DigitalOcean App Platform | More control, scalable pricing |
| Heroku | Requires credit card for add-ons |
| Vercel | Requires serverless architecture adaptation |
Planned features for future versions:
- 💳 Payment Processing — Stripe/Razorpay integration for real booking flows with reservation confirmation emails
- 🗓 Availability Calendar — Date-range picker preventing double-bookings (stored in MongoDB)
- 💾 Persistent Wishlist — Migrate from
req.sessionto per-user MongoDB collection - 📧 Email Notifications — Nodemailer/SendGrid for booking alerts and password resets
- ⚡ Real-Time Updates — Socket.io for live host notifications when a booking request arrives
- 🏆 Superhost Badge System — Auto-compute host tier based on review average and response rate
- 🔎 Advanced Search — Price range slider, amenity filters, geospatial radius search
- 🧑💼 Admin Dashboard — Moderation panel for users, listings, and review management
- 🌐 i18n & Localization — Multi-language support and currency conversion
- 📲 PWA Support — Service worker, offline mode, install-to-homescreen capability
Contributions are welcome and appreciated! Please follow the workflow below:
- Fork the repository on GitHub
- Clone your fork locally:
git clone https://github.com/<your-username>/rajspace.git - Create a feature branch:
git checkout -b feature/your-feature-name - Commit your changes using Conventional Commits:
feat: add Stripe payment integration fix: correct session cookie expiry calculation docs: update environment variable table - Push to your fork:
git push origin feature/your-feature-name - Open a Pull Request against the
mainbranch with a clear description of your changes
Tip: For major architectural changes, open an Issue first to discuss the approach before building.
How do I log in with the seeded demo account?
Run
npm run seed first, then login with:• Username:
rajspace-seed• Password:
seed-pass-123
Where do I get the Mapbox API token?
Register at mapbox.com → Account → Tokens → Create a public token. Use it as
MAP_TOKEN in your .env.
Where do I get the Cloudinary credentials?
Register at cloudinary.com → Dashboard → Copy Cloud Name, API Key, and API Secret into your
.env.
Can I run the app without Cloudinary?
Yes. If the three Cloudinary environment variables are absent, the app falls back to
multer.memoryStorage() and new listings display a high-quality Unsplash placeholder image instead.
What happens to reviews when a listing is deleted?
A Mongoose
findOneAndDelete post-hook on the Listing model automatically calls Review.deleteMany() with all linked review IDs, ensuring no orphaned documents remain in the database.
Is the wishlist persistent across devices?
Currently, wishlists are stored in
express-session (server-side session in MongoDB). They persist across browser restarts on the same device/browser, but are not tied to a user account across different devices. Persistent per-user wishlists are on the roadmap.
This project is licensed under the ISC License. See the LICENSE file for full terms.





