Yelp Camp
personal

Yelp Camp

Campground Review Platform

6 weeks
Individual Project
Node.jsExpress.jsMongoDBEJSPassport.jsBootstrapCloudinary

As part of my journey in a web development course, I built YelpCamp: a project that showcases my skills in full-stack development. This application is a result of my passion for coding and learning new technologies. Built with MongoDB for database management, Express.js and Node.js for the backend, and EJS for templating, YelpCamp allows users to post, view, and review campgrounds. For user authentication, I integrated Passport.js, ensuring a secure user experience. The UI was designed with Bootstrap for a responsive layout. I've also utilized Cloudinary for image uploads, enhancing the visual appeal of campground listings.

Key Features

Campground Management

Complete CRUD operations for campgrounds with detailed information, location data, pricing, and comprehensive campground profiles.

Review System

User-generated review and rating system with star ratings, detailed comments, and review management for authentic campground feedback.

User Authentication

Secure user authentication and authorization using Passport.js with registration, login, and protected routes for user security.

Image Upload

Cloudinary integration for image upload, storage, and optimization with multiple image support and automatic image processing.

Interactive Maps

Mapbox integration for interactive campground location visualization with custom markers and location-based features.

Responsive Design

Bootstrap-powered responsive design ensuring optimal user experience across all devices and screen sizes.

Development Journey

Phase 1

Backend Setup

Set up the Express.js server, MongoDB database connection, and basic routing structure with RESTful API design principles.

Phase 2

Authentication System

Implemented user authentication with Passport.js, session management, and authorization middleware for protected routes.

Phase 3

CRUD Operations

Developed complete CRUD functionality for campgrounds and reviews with proper data validation and error handling.

Phase 4

Advanced Features

Added image upload with Cloudinary, interactive maps with Mapbox, and responsive design with Bootstrap styling.

Challenges & Solutions

Image Upload Management

Problem:

Implementing efficient image upload and storage with proper file handling, validation, and cloud storage integration.

Solution:

Integrated Cloudinary for cloud-based image storage with automatic optimization, multiple image support, and proper error handling for upload failures.

Authentication & Authorization

Problem:

Implementing secure user authentication with proper session management and role-based access control for campground operations.

Solution:

Used Passport.js for authentication with bcrypt password hashing, session-based login, and middleware for protecting routes and ensuring user authorization.

Geolocation Integration

Problem:

Adding interactive maps and location-based features with accurate geocoding and responsive map visualization.

Solution:

Integrated Mapbox for interactive maps with geocoding API for location conversion, custom markers, and responsive map design across devices.

campground.js
javascript
// Campground model with Mongoose
export const campgroundSchema = new Schema({
    title: {
        type: String,
        required: true
    },
    images: [{
        url: String,
        filename: String
    }],
    geometry: {
        type: {
            type: String,
            enum: ['Point'],
            required: true
        },
        coordinates: {
            type: [Number],
            required: true
        }
    },
    price: Number,
    description: String,
    location: String,
    author: {
        type: Schema.Types.ObjectId,
        ref: 'User'
    },
    reviews: [{
        type: Schema.Types.ObjectId,
        ref: 'Review'
    }]
});

// Route for creating new campground
router.post('/', isLoggedIn, upload.array('image'), validateCampground, catchAsync(async (req, res, next) => {
    export const geoData = await geocoder.forwardGeocode({
        query: req.body.campground.location,
        limit: 1
    }).send();
    
    export const campground = new Campground(req.body.campground);
    campground.geometry = geoData.body.features[0].geometry;
    campground.images = req.files.map(f => ({ url: f.path, filename: f.filename }));
    campground.author = req.user._id;
    
    await campground.save();
    req.flash('success', 'Successfully made a new campground!');
    res.redirect(`/campgrounds/${campground._id}`);
}));

// Authentication middleware
export const isLoggedIn = (req, res, next) => {
    if (!req.isAuthenticated()) {
        req.session.returnTo = req.originalUrl;
        req.flash('error', 'You must be signed in first!');
        return res.redirect('/login');
    }
    next();
}