Food Reads is a web app that connects food lovers by allowing them to review cook books. To explore Food Reads click here.
Food Reads was built using Express for the server with a postgreSQL database. The back end structure utilizes RESTful convention and handles user requests through our API and modifies the browser through AJAX. Food Reads is session based and uses BCrypt to safely store user passwords and verify login credentials.
The front end was built using Pug to render the pages with JavaScript and AJAX to make the pages dynamic.
- Express
- BCrypt
- PostgreSQL
- Heroku
- Pug
- Add/ delete shelves
- Add/delete book from shelves
- Add comments and reviews to books
User authentication is handled in JavaScript using BCrypt to hash passwords for storage. To authenticate users, the submitted password is hashed and then compared to the hashed password stored in the database.
router.post(
"/",
loginValidator,
asyncHandler(async (req, res, next) => {
const { email, password } = req.body;
const validationErrors = validationResult(req);
let errors = [];
if (validationErrors.isEmpty()) {
const user = await User.findOne({
where: {
email,
},
});
if (user) {
validPassword = await bcrypt.compare(
password,
user.hashedPassword.toString()
);
if (validPassword) {
loginUser(req, res, user);
return req.session.save((err) => {
if (err) next(err);
else {
return res.redirect("/home");
}
});
}
}
In order for the user to log in, we first check to see if the inputs are valid. Then, we find the user in the database based on their email. If we are able to find a user, then we hash their input password and compare it to the hashed password stored in the server. Finally, if the hashed input password matches the stored hashed password, the user is logged in with their session persisted and redirected to home page.
Sessions are stored server side using Sequelize.js. For actions that require authorization, the server verifies that a cookie with a matching user id as the user exists in the storage. Upon verification that a session does exist for that user, the user is then allowed to perform CRUD operations. If no such session exists in the storage, then user is redirected to the login page.
// This route is used to login a demo user /login/demo
router.get('/demo', asyncHandler(async (req, res, next) => {
//! Find Demo User in the Database
const user = await User.findOne({
where: {
email: "demo@gmail.com"
},
});
//! Log the Demo User in
loginUser(req, res, user);
// Save to session and redirect user to home page
return req.session.save((err) => {
if (err) next(err);
else {
return res.redirect("/home");
}
});
}));
When the user makes a request, a fetch containing form data is made to our API. If the request is valid, changes are made in the database to reflect the user request. After completion of the user request, a script on the user side updates the browser to display these changes without refreshing the page or redirecting the user.
if (document.querySelector("#addToShelf")) {
document
.querySelector("#addToShelf")
.addEventListener("click", async (event) => {
event.preventDefault();
const form = new FormData(document.querySelector("#shelf-form"));
const shelfId = form.get("shelfId");
const bookId = form.get("bookId");
const res = await fetch("https://food-reads.herokuapp.com/shelf", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ shelfId, bookId }),
});
if (res.ok) {
const shelfAdd = document.querySelector(".shelf-add-div");
shelfAdd.innerText = "This book is now in your shelf";
}
});
}
One thing we had to take into consideration was adding a conditional statement to verify that the submit button existed on the web page. Without that conditional statement, the script would hit an error on pages where the button had been replaced by text that signified that the book had already been added to the user shelf.
Allow users to:
- update profile information such as their email
- update ratings and comments
- delete ratings and comments
- add a new book to the site
- reply to other comments
- upvote/downvote comments
- perform searches
- tags