Wanderlust is a full-stack web application inspired by the core experience of platforms like Airbnb, where users can explore property listings, create their own listings, upload images, write reviews, and manage their content through an authenticated account.
This project was built as a hands-on learning project while following the web development course tutorials by Apna College. The project gave me practical experience with backend development, databases, authentication, authorization, MVC architecture, server-side rendering, image handling, validation, middleware, and full-stack application structure.
Learning Project: This repository represents my implementation and practice while following the Apna College course. The project idea and overall development flow were based on the course material.
Wanderlust allows users to interact with a travel/property listing platform through a complete web application.
The application provides functionality for:
- ๐ค User registration and login
- ๐ Authentication and authorization
- ๐ Creating property listings
- โ๏ธ Editing existing listings
- ๐๏ธ Deleting listings
- ๐ท Uploading listing images
- โ๏ธ Cloud-based image storage using Cloudinary
- โญ Creating reviews
- ๐๏ธ Deleting reviews
- ๐ Listing ownership management
- โ Server-side validation
โ ๏ธ Custom error handling- ๐ฌ Flash messages
- ๐ Session management
- ๐๏ธ MongoDB database integration
- ๐งฉ MVC-based backend architecture
- ๐จ Dynamic EJS views
| Technology | Purpose |
|---|---|
| Node.js | JavaScript runtime |
| Express.js | Web server and routing |
| MongoDB | Database |
| Mongoose | MongoDB object modeling |
| EJS | Server-side rendering |
| EJS-Mate | EJS layouts and reusable templates |
| Technology | Purpose |
|---|---|
| Passport.js | Authentication framework |
| Passport Local | Username/password authentication |
| Passport Local Mongoose | Authentication integration with Mongoose |
| Express Session | Session management |
| Cookie Parser | Cookie handling |
| Connect Flash | Success/error messages |
| Technology | Purpose |
|---|---|
| Joi | Request/data validation |
| ExpressError | Custom application errors |
| wrapAsync | Async error handling |
| Middleware | Authentication, authorization and validation |
| Technology | Purpose |
|---|---|
| Multer | Handling multipart/form-data uploads |
| Cloudinary | Cloud-based image storage |
| Multer Memory Storage | Processing uploaded files in memory |
| Technology | Purpose |
|---|---|
| EJS | Dynamic HTML rendering |
| EJS-Mate | Layouts and reusable templates |
| HTML/CSS | User interface |
| JavaScript | Client-side functionality |
Users can create an account, log in, and log out.
Authentication is implemented using:
- Passport.js
- Passport Local Strategy
- Passport Local Mongoose
- Express Session
- Serialized and deserialized users
The application stores the authenticated user's identity in the session and makes the current user available throughout the application.
Authentication and authorization are handled separately.
The application verifies that:
- A user is logged in before performing protected actions.
- Only the owner of a listing can edit or delete it.
- Only the author of a review can delete it.
This introduced practical experience with resource ownership and authorization middleware.
Users can perform complete CRUD operations on listings.
Authenticated users can create a new listing containing information such as:
- Title
- Description
- Price
- Location
- Country
- Image
Users can:
- View all listings
- Open an individual listing
- View associated reviews
- View listing ownership information
Listing owners can edit their listings.
Listing owners can delete their listings.
Listings can contain multiple reviews.
A review is associated with:
- A listing
- An author
- Review information
The relationship is represented using MongoDB ObjectId references.
When a listing is displayed, its reviews and review authors are populated using Mongoose.
This provided practical experience with:
- One-to-many relationships
- MongoDB references
- Mongoose
populate() $pull- Related document management
One of the important parts of this project was implementing image uploading.
The application uses Multer to receive uploaded files and Cloudinary for cloud-based image storage.
The general flow is:
User selects image
โ
Multipart form submission
โ
Multer
โ
Memory Storage
โ
Image Buffer
โ
Cloudinary Upload Stream
โ
Cloudinary URL + Public ID
โ
MongoDB Listing Document
The listing stores the Cloudinary information as:
image
โโโ url
โโโ filename
This helped me understand how file uploads can be separated from the application's main database storage.
The application uses MongoDB with Mongoose.
The main models are:
User
โ
โโโ owns โโโโโโโโโโโโบ Listings
โ
โโโ contains โโโโโโบ Reviews
โ
โโโ written by User
Handles application users and authentication information.
Stores property/travel listing information.
A listing contains references to:
- Its owner
- Its reviews
Stores review information and references the user who created it.
The project uses MongoDB references through ObjectId.
For example, a listing contains:
owner: {
type: Schema.Types.ObjectId,
ref: "User"
}and reviews are represented through:
reviews: [
{
type: Schema.Types.ObjectId,
ref: "Review"
}
]The application then uses Mongoose population to retrieve related documents.
For example:
.populate({
path: "reviews",
populate: {
path: "author"
}
})
.populate("owner")This allowed me to work with relationships between MongoDB collections rather than keeping all information inside a single document.
The project also handles dependent data.
When a listing is deleted, its associated reviews are removed through a Mongoose post middleware:
listingSchema.post("findOneAndDelete", async (listing) => {
if (listing) {
await Review.deleteMany({
_id: {
$in: listing.reviews
}
});
}
});This introduced the concept of maintaining referential consistency when working with related MongoDB documents.
The backend follows an MVC-inspired structure.
Request
โ
โผ
Routes
โ
โผ
Middleware
โ
โผ
Controller
โ
โผ
Model
โ
โผ
MongoDB
โ
โผ
Controller
โ
โผ
EJS View
โ
โผ
Response
The application separates responsibilities into:
models/
controllers/
routes/
views/
utils/
middleware.js
This made the application easier to organize compared with putting all application logic inside a single server file.
Middleware is used extensively throughout the application.
Some of the important middleware responsibilities include:
isloggedIn
Checks whether a user is authenticated before accessing protected resources.
isOwner
Checks whether the currently authenticated user owns a listing.
isReviewAuthor
Ensures that only the review author can delete their review.
validateListing
validateReview
Validates incoming request data using Joi.
saveRedirectUrl
Stores the original URL when an unauthenticated user tries to access a protected route, allowing the application to redirect them back after authentication.
The application uses Joi for server-side validation.
Validation is performed before data reaches the controller.
Request
โ
Joi Validation
โ
Valid โโโโโโโโบ Controller
โ
โโโ Invalid โโโโบ ExpressError
This helped me understand why validation should not depend only on frontend forms.
Server-side validation provides an additional layer of protection for incoming data.
The project includes custom error handling using:
ExpressError
wrapAsync
Centralized Error Middleware
The wrapAsync utility helps handle errors from asynchronous controller functions without repeatedly writing the same try/catch structure.
A custom ExpressError class is used to provide:
- HTTP status codes
- Meaningful error messages
The application also contains a centralized Express error-handling middleware.
The project uses connect-flash to provide user feedback after operations.
Examples include:
Successfully created a new listing!
Successfully updated the listing!
Successfully deleted the listing!
Created new review!
Successfully deleted review!
You must be signed in first!
These messages are passed to EJS through res.locals.
Instead of using React for the frontend, this project uses EJS (Embedded JavaScript Templates) for server-side rendering.
The views are organized into:
views/
โโโ layouts/
โ โโโ boilerplate.ejs
โ
โโโ includes/
โ โโโ flash.ejs
โ โโโ footer.ejs
โ โโโ navbar.ejs
โ
โโโ listings/
โ โโโ index.ejs
โ โโโ show.ejs
โ โโโ new.ejs
โ โโโ edit.ejs
โ
โโโ users/
โ โโโ login.ejs
โ โโโ signup.ejs
โ
โโโ error.ejs
Using EJS helped me understand how dynamic server-rendered applications work and how backend data can be passed directly into templates.
Wanderlust/
โ
โโโ controllers/
โ โโโ listings.js
โ โโโ reviews.js
โ โโโ users.js
โ
โโโ init/
โ โโโ data.js
โ โโโ index.js
โ
โโโ models/
โ โโโ listing.js
โ โโโ review.js
โ โโโ user.js
โ
โโโ public/
โ โโโ css/
โ โ โโโ rating.css
โ โ โโโ styles.css
โ โ
โ โโโ js/
โ โโโ script.js
โ
โโโ routes/
โ โโโ listing.js
โ โโโ review.js
โ โโโ user.js
โ
โโโ uploads/
โ
โโโ utils/
โ โโโ ExpressError.js
โ โโโ wrapAsync.js
โ
โโโ views/
โ โโโ includes/
โ โ โโโ flash.ejs
โ โ โโโ footer.ejs
โ โ โโโ navbar.ejs
โ โ
โ โโโ layouts/
โ โ โโโ boilerplate.ejs
โ โ
โ โโโ listings/
โ โ โโโ edit.ejs
โ โ โโโ index.ejs
โ โ โโโ new.ejs
โ โ โโโ show.ejs
โ โ
โ โโโ users/
โ โ โโโ login.ejs
โ โ โโโ signup.ejs
โ โ
โ โโโ error.ejs
โ
โโโ .env
โโโ app.js
โโโ cloudConfig.js
โโโ middleware.js
โโโ package.json
โโโ package-lock.json
โโโ schema.js
A typical listing creation request follows this architecture:
Browser
โ
โผ
POST /listings
โ
โผ
Authentication Middleware
โ
โผ
Joi Validation
โ
โผ
Multer
โ
โผ
Cloudinary
โ
โผ
Listing Controller
โ
โผ
Mongoose
โ
โผ
MongoDB
โ
โผ
Flash Message
โ
โผ
Redirect
โ
โผ
Listings Page
This project therefore gave me practical exposure to how different layers of a web application communicate with one another.
Building this project allowed me to practice a broad range of web development concepts.
- Node.js fundamentals
- Modules
- Async/await
- Promises
- Error handling
- Environment variables
- Express application setup
- Routing
- Route parameters
- Middleware
- Request/response handling
- Static files
- HTTP methods
- Method override
- Error-handling middleware
- GET
- POST
- PUT
- DELETE
- CRUD operations
- REST-style resource routes
- MongoDB databases
- Collections
- Documents
- Mongoose schemas
- Mongoose models
- ObjectId references
- Relationships
- Population
- Querying
- Updating documents
- Deleting documents
$pull- Middleware/hooks
- Passport.js
- Local Strategy
- Passport Local Mongoose
- Sessions
- Serialization
- Deserialization
- Login/logout
- Protected routes
- Ownership verification
- Resource-level authorization
- Review author verification
- Authentication vs authorization
- HTML
- CSS
- JavaScript
- EJS
- EJS layouts
- EJS partials
- Dynamic rendering
- Forms
- Multipart forms
- Multer
- Memory storage
- Buffers
- Cloudinary
- Upload streams
- Cloud-based image storage
- Joi validation
- Custom errors
- Async error handling
- Express error middleware
- Flash messages
The biggest value of this project was not simply getting a website to work.
It helped me understand how the individual parts of a full-stack application fit together.
I practiced moving from:
User Interface
โ
HTTP Request
โ
Route
โ
Middleware
โ
Controller
โ
Model
โ
Database
โ
Response
โ
Rendered View
I also gained a better understanding of why applications are separated into different layers and how authentication, authorization, validation, database relationships, file uploads, and error handling work together in a real application.
Sensitive configuration should not be committed to GitHub.
The project uses environment variables through dotenv and Cloudinary configuration.
Create a .env file locally with the required configuration.
Example:
CLOUDINARY_URL=your_cloudinary_configurationNever commit your actual credentials, API keys, passwords, or secrets to the repository.
For a public repository, a .env.example file can be added containing only placeholder values.
git clone <your-repository-url>
cd Wanderlustnpm installCreate a .env file and add your required Cloudinary configuration.
Make sure MongoDB is running locally.
The development database used by the application is:
mongodb://127.0.0.1:27017/wanderlust
node app.jsThe application runs on:
http://localhost:8080
Some of the major packages used in this project include:
express
mongoose
ejs
ejs-mate
passport
passport-local
passport-local-mongoose
express-session
connect-flash
multer
cloudinary
joi
method-override
dotenv
cookie-parser
Although the application already covers a wide range of full-stack concepts, several improvements could be added in the future:
- ๐ Search and filtering
- ๐บ๏ธ Interactive maps
- ๐ Location-based search
- โญ Improved rating system
- ๐ฑ More responsive UI
- โก Better frontend interactions
- ๐ผ๏ธ Multiple-image galleries
- ๐ More advanced notifications
- ๐งช Automated testing
- ๐ Production deployment
- ๐ Additional security hardening
- ๐ Pagination
- ๐งโ๐ผ Improved user profile management
- ๐ณ Booking/payment functionality
This project was developed during my learning journey through the Apna College web development course.
The project was built by following the course's video-based development process and implementing the concepts hands-on.
Rather than treating the project as only a final application, I used it as an opportunity to understand and practice the technologies and concepts involved in building a complete web application.
Node.js
Express.js
MongoDB
Mongoose
EJS
MVC Architecture
REST APIs / RESTful Routing
CRUD
Authentication
Authorization
Sessions
Passport.js
Middleware
Joi Validation
Error Handling
Multer
Cloudinary
Server-Side Rendering
Database Relationships
Credit: Apna College โ Web Development Course
This repository is not a MERN application because React is not used in this project.
The frontend of Wanderlust is implemented using EJS server-side rendering, while the backend is built with Node.js + Express.js and the database uses MongoDB/Mongoose.
I have separately studied React as part of my broader web development learning journey, but React is not part of this particular project.
Computer Science Student | Data Science & AI Enthusiast | Full-Stack Development Learner
- ๐ป GitHub: AliMuhammad78
- ๐ผ LinkedIn: Muhammad Ali
- ๐ Kaggle: ali98muhammad45
Wanderlust represents one of my major hands-on full-stack learning projects.
Through this project, I moved beyond individual tutorials and practiced connecting multiple technologies into one working application โ from authentication and routing to database relationships, validation, image uploads, cloud storage, error handling, and server-side rendering.
It provided practical experience with the architecture and development workflow involved in building a real-world web application from the ground up.
If you find the project useful or interesting, consider โญ starring the repository.