A website that brings together all available campgrounds with user reviews so you can select the best from the best.

YelpCamp is a subtle clone of Yelp, a popular website used to rate and review Restaurants, Shopping, Nightlife, Food, Entertainment, Things to Do, Services, etc. The YelpCamp website provides you with similar services for campgrounds. Whenever you make any plans to go camping, all you need to do is, go to the site and check from the available options and select the one best suitable for you. User comments on these campgrounds will tell you about the reviews and make the selection process much easier.

image1

image2

image3

image4

Tech Stack

The front-end is designed using the features provided in Bootstrap and the back-end is implemented in Node.js using the Express framework. Passport is used to implement authentication and authorization, and all the flash messages are implemented using the connect-flash npm package.

Implementation

  • The Landing Page

This page contains a single button to go to the main website where information regarding all the campgrounds is available.

  • The Feed

This page contains all the registered campgrounds. These campgrounds are fetched from the database using the .find() method of mongoose and sent to the index.ejs file. This file then loops through all the camprgounds and displays them. This page is rendered dynamically as it depends on the information stored in the database.

router.get("/", function(req, res) {
	Campground.find({}, function(err, allCampgrounds) {
		if(err) {
			console.log(err);
		} else {
			res.render("campgrounds/index", {campgrounds: allCampgrounds});
		}
	});
});
  • The Show Page

This page views individual campgrounds and their respective comments.

  • Edit/Delete Campground

This option is provided for all the campgrounds but can only be implemented by the user that created the campground. The visibility of these buttons is controlled by the UI by comparing the ids of the user and the author of the campground.
However, even if a user manages to reach the edit/delete page by manipulating the URL using a service like Postman, only authenticated users will be able to complete the request. This authentication is maintained using a middleware logic which ensures that the user is signed in and the rightful owner of that campground.

router.get("/:id/edit", middleware.isLoggedIn, middleware.checkCampgroundOwnership, function(req, res) {
	Campground.findById(req.params.id, function(err, foundCampground) {
		res.render("campgrounds/edit", {campground: foundCampground});
	});
});

The middleware which checks for the logged in logic is implemeneted using the .isAuthenticated method provided in Passport.

isLoggedIn: function(req, res, next) {
	if(req.isAuthenticated()) {
		return next();
	}
	req.flash("error", "You need to be logged in to do that");
	res.redirect("/login");
}

The middlware used for checking rightful ownership of the campground compares the author.id and the user._id of the logged in user. Only if they match, can the user edit or delete a campground.

middlewareObj.checkCampgroundOwnership = function(req, res, next) {
	if(req.isAuthenticated()) {
		Campground.findById(req.params.id, function(err, foundCampground) {
			if(err || !foundCampground) {
				req.flash("error", "Campground not found");
				res.redirect("back");
			} else {
				if(foundCampground.author.id.equals(req.user._id)) {
					next();
				} else {
					req.flash("error", "You don't have permission to do that");
					res.redirect("back");
				}
			}
		});
	} else {
		req.flash("error", "You need to be logged in to do that");
		res.redirect("back");
	}
}
  • Edit/Delete Comments

Similar to campgrounds, a user can only delete their comments. The UI makes these options visible to the user only if they are the owner of that comment. The middleware to check comment ownership is similar to that used for campgrounds, as can be seen below.

middlewareObj.checkCommentOwnership = function(req, res, next) {
	if(req.isAuthenticated()) {
		Comment.findById(req.params.comment_id, function(err, foundComment) {
			if(err || !foundComment) {
				req.flash("error", "Comment not found");
				res.redirect("back");
			} else {
				if(foundComment.author.id.equals(req.user._id)) {
					next();
				} else {
					req.flash("error", "You don't have permission to do that");
					res.redirect("back");
				}
			}
		});
	} else {
		req.flash("error", "You need to be logged in to do that");
		res.redirect("back");
	}
}

Database models

There are three different models used, one for the users, one for the campgrounds and one for the comments.

  • User Model
    The user schema is as follows:
var UserSchema = new mongoose.Schema({
	username: String,
	password: String
});
  • Comment Model
    The comments schema is as follows:
var commentSchema = mongoose.Schema({
    text: String,
    author: {
		id: {
			type: mongoose.Schema.Types.ObjectId,
			ref: "User"
		},
		username: String
	}
});
  • Campground Model
    The campground schema is as follow:
var campgroundSchema = new mongoose.Schema ({
	name: String,
	price: String,
    image: String,
    description: String,
	author: {
		id: {
			type: mongoose.Schema.Types.ObjectId,
			ref: "User"
		},
		username: String
	},
    comments: [
		{
			type: mongoose.Schema.Types.ObjectId,
			ref: "Comment"
		}
	]
});

Demo

🌒 Click here for the Live Demo.