Restaurant Recommendation using MERN
Last Updated :
15 Nov, 2023
This article is about Restaurant Recommendations using the MERN (MongoDB, Express.js, React.js, Node.js) stack. This project displays a list of restaurants to the users. Users can apply filters like location, cuisines, and ratings based on filters applied by the user, the user gets recommended specific restaurants.
Preview of final output: Let us have a look at how the final output will look like.
Restaurant-Recommendation
Prerequisites:
Approach:
- In Frontend, i have used two components Navbar which contains the name of the title and Card component which contains the UI of the restaurant details.I have used custom hook useRestaurantContext which contains the details of the filters selected by the users.
- In Backend, i have created one api route /api/restaurants which gets all the restaurants data from the database and if we pass locatin, rating and cuisines data in the request body then it gets the data from the database based on filters.
Functionalities of Restaurant Recommendation:
- Get All Restaurants data: Initially user can see all the list of restaurants which present in the database.
- Location: User can select a location from the location dropdown.Based on the location selected by the user , list of restaurants gets changed. User can only the restaurants that contains the location selected by the user.
- Cuisines: User can select a cuisine from the Cuisines dropdown.Based on the selected cuisines the list gets updated.
- Rating: User can select a rating such as above 3 and above 4. If user selects above 3 then user gets list of restaurants above 3 rating and same goes for above 4.
Project Structure:
Full Project structure
Steps to create project:
Step 1: create a directory for project
mkdir Restaurant-Recommendation
Step 2: Create Sub directories for frontend and backend
Step 3: open Restaurant-Recommendation using the command
cd Restaurant-Recommendation
Step 4: Create seperate directories for frontend and backend
mkdir frontend
mkdir backend
Step 5: open backend directory using the command
cd backend
Step 6: Initialize Node Package Manager using the command.
npm init -y
Step 5: Install express, cors, body-parser, dotenv, mongoose packages using following command.
npm i express cores body-parser mongoose dotenv
Note: In backend create a file named .env and add variable
PORT = 4000 and MONGO_URL = specifiy your mongodb url connection.
Step 6: Move to Restaurant-Recommendation (Main folder) using the command.
cd ../
Step 7: Now move to frontend file using the following command.
cd frontend.
Step 8: Create a React-application in frontend directory using the following command.
npx create-react-app .
Step 9: Install bootstrap and axios packages
npm i axios react-bootstrap
Step 10: Open the Restaurant-Recomendation using your familiar code editor.
Step 11: Navigate to frontend folder.
Step 12: Add the files as per project structure with below code.
Step 13: Navigate to backend directory
Step 14: Add the files as per project structure with below code.
The updated dependencies in package.json for frontend will look like:
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.5.1",
"bootstrap": "^5.3.2",
"react": "^18.2.0",
"react-bootstrap": "^2.9.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}
The updated dependencies in package.json file for backend will look like:
"dependencies": {
"bcrypt":"^5.1.1"
"cors": "^2.8.5",
"express": "^4.18.2",
"mongoose": "^8.0.0",
"nodemon": "^3.0.1",
}
Example: Write the following code in respetive files
Frontend: In Frontend, i have used two components Navbar which contains the name of the title and Card component which contains the UI of the restaurant details.I have used custom hook useRestaurantContext which contains the details of the filters selected by the users.
Project Structure:
Frontend Project Structure
- App.js : This is the main file which contains the Navabar component ,filters like location,rating,cuisines and CardItem component which contains the information of each restaurant in card format.
- NavBar.js : This file contain the navbar which has the name of the project.
- CardItem : This file contains the code to fetch the restaurant data from the backend based on the filters selected by the user and present the each restaurant details to the user in card format.
- useContext.js : This file contains the context api code where the filters like location,rating and cuisines are accessed as global state.
- useRestaurant.js : This file contains the code for the custom hook which is used to access the state of location,rating,cuisines at any component in the project.
Javascript
import style from "./App.module.css" ;
import { Form } from "react-bootstrap" ;
import NavBar from "./components/NavBar/NavBar" ;
import { useState } from "react" ;
import CardItem from "./components/RestaurantCard/Card" ;
import useRestaurantContext from "./components/Hooks/useRestaurant" ;
function App() {
const { selectedItems, setSelectedItems, setLocation, setRating } =
useRestaurantContext();
const options = [
{ value: "North Indian" , label: "North Indian" },
{ value: "South Indian" , label: "South Indian" },
{ value: "Chinese" , label: "Chinese" },
{ value: "Desserts" , label: "Desserts" },
{ value: "Italian" , label: "Italian" },
{ value: "Oriental" , label: "Oriental" },
{ value: "Pastas" , label: "Pastas" },
{ value: "Pizzas" , label: "Pizzas" },
{ value: "Japanese" , label: "Japanese" },
{ value: "Sushi" , label: "Sushi" },
{ value: "Barbecue" , label: "Barbecue" },
{ value: "Steak" , label: "Steak" },
{ value: "Seafood" , label: "Seafood" },
];
const handleCheckboxChange = (value) => {
if (selectedItems.includes(value)) {
setSelectedItems(selectedItems.filter((item) => item !== value));
} else {
setSelectedItems([...selectedItems, value]);
}
console.log( "elements" , selectedItems);
};
return (
<div className= "App" >
<NavBar />
<div>
<div className={style.headers}>
<div className={style.locationContainer}>
<Form.Select
aria-label= "Location"
onChange={(e) => {
setLocation(e.target.value);
}}
>
<option hidden>Select Location</option>
<option value= "Hyderabad" >Hyderabad</option>
<option value= "Banglore" >Banglore</option>
<option value= "Mumbai" >Mumbai</option>
<option value= "Delhi" >Delhi</option>
<option value= "Pune" >Pune</option>
<option value= "Chennai" >Chennai</option>
</Form.Select>
</div>
<div className={style.cuisinesContainer}>
<Form>
<Form.Label>Select Cuisines:</Form.Label>
{options.map((option) => (
<Form.Check
key={option.value}
type= "checkbox"
id={option.value}
label={option.label}
checked={selectedItems?.includes(option.value)}
onChange={() => handleCheckboxChange(option.value)}
/>
))}
</Form>
</div>
<div className={style.ratingContainer}>
<Form.Select
aria-label= "Default select example"
onChange={(e) => {
setRating(e.target.value);
}}
>
<option hidden>Select Rating</option>
<option value= "3" >3 above</option>
<option value= "4" >4 above</option>
</Form.Select>
</div>
</div>
</div>
<div className={style.restaurants}>
<h3>Restaurants</h3>
<CardItem />
</div>
</div>
);
}
export default App;
|
Javascript
import React from "react" ;
import { Nav, Navbar, Container } from "react-bootstrap" ;
export default function NavBar() {
return (
<div>
<Navbar bg= "dark" data-bs-theme= "dark" >
<Container>
<Navbar.Brand href= "#home" >
Restro - find your restaurant
</Navbar.Brand>
<Nav className= "me-auto" ></Nav>
</Container>
</Navbar>
</div>
);
}
|
Javascript
import Card from "react-bootstrap/Card" ;
import ListGroup from "react-bootstrap/ListGroup" ;
import useRestaurantContext from "../Hooks/useRestaurant" ;
import { useEffect, useState } from "react" ;
import axios from "axios" ;
function CardItem() {
const { location, rating, selectedItems } = useRestaurantContext();
const [restaurants, setRestaurants] = useState([]);
useEffect(() => {
getAllRestaurants();
}, [location, rating, selectedItems]);
const getAllRestaurants = async () => {
axios
location: location,
rating: rating,
cuisines: selectedItems,
})
.then(
(response) => {
console.log( "response is" , response);
if (response.data.success) {
setRestaurants(response.data.data);
}
},
(error) => {
console.log( "error is " , error);
}
);
};
return (
<div style={{ display: "flex" , flexWrap: "wrap" , marginLeft: "2vw" }}>
{restaurants.map((item) => (
<Card style={{ width: "18rem" , marginLeft: "4vw" , marginTop: "4vh" }}>
<Card.Img
variant= "top"
src={item.image}
style={{ height: "20vh" , backgroundCover: "cover" }}
/>
<Card.Body>
<Card.Title>{item.name}</Card.Title>
<Card.Text>
Types of food we offer :{ " " }
{item.cuisines?.map((foodItem) => foodItem + ", " )}
</Card.Text>
</Card.Body>
<ListGroup className= "list-group-flush" >
<ListGroup.Item>Address:{item.address}</ListGroup.Item>
<ListGroup.Item>City:{item.location}</ListGroup.Item>
<ListGroup.Item>Rating:{item.rating}</ListGroup.Item>
</ListGroup>
</Card>
))}
</div>
);
}
export default CardItem;
|
Javascript
import { createContext, useContext, useState } from "react" ;
const RestaurantContext = createContext();
const Provider = ({ children }) => {
const [selectedItems, setSelectedItems] = useState([]);
const [location, setLocation] = useState();
const [rating, setRating] = useState();
const data = {
selectedItems,
setSelectedItems,
location,
setLocation,
rating,
setRating,
};
return (
<RestaurantContext.Provider value={data}>
{children}
</RestaurantContext.Provider>
);
};
export { Provider };
export default RestaurantContext;
|
Javascript
import { useContext } from "react" ;
import RestaurantContext from "../Context/useContext" ;
const useRestaurantContext = () => {
return useContext(RestaurantContext);
};
export default useRestaurantContext;
|
CSS
.headers {
display : flex;
align-items: center ;
justify- content : space-between;
margin-left : 5 vw;
margin-right : 5 vw;
margin-top : 3 vh;
padding-bottom : 2 vh;
border-bottom : 1px solid gray ;
}
.locationContainer {
width : 10 vw;
}
.cuisinesContainer {
margin-top : 5 vh;
width : 15 vw;
height : 10 vh;
overflow-y: scroll ;
}
.ratingContainer {
width : 10 vw;
}
.restaurants {
padding-bottom : 3 vh;
}
.restaurants h 3 {
margin-left : 5 vw;
margin-top : 1 vh;
margin-bottom : 1 vh;
}
|
Backend: In Backend, i have created one api route /api/restaurants which gets all the restaurants data from the database and if we pass locatin, rating and cuisines data in the request body then it gets the data from the database based on filters.
Project Structure:
Backend Project Structure
- index.js : This file contains the main code which involves connecting to mongodb database and calling apis.
- Restaurant.model.js : This file contains the schema design of restaurant model.
- restaurant.route.js : This file contains the code for the api route to fetch the data from the database based on filters applied by the user.
- restaurant.controller.js : This file contains the code for the api route to fetch the data from the database.
Javascript
const express = require( "express" );
const mongoose = require( "mongoose" );
const bodyParser = require( "body-parser" );
const cors = require( "cors" );
require( "dotenv" ).config();
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
mongoose
.connect(
useNewUrlParser: true ,
useUnifiedTopology: true ,
})
.then(() => {
console.log( "Connected to MongoDB" );
})
. catch ((error) => {
console.log( "Failed to connect to MongoDB" , error);
});
app.get( "/health" , (req, res) => {
res.status(200).json( "Server is up and running" );
});
const PORT = process.env.PORT || 4000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
const restaurantRoutes = require( "./routes/restaurant.route" );
app.use( "/api" , restaurantRoutes);
|
Javascript
const mongoose = require( "mongoose" );
const restaurantSchema = new mongoose.Schema({
name: String,
address: String,
contact: String,
location: String,
rating: Number,
offers: Boolean,
cuisines: [String],
image: String,
});
module.exports = mongoose.model( "restaurant" , restaurantSchema);
|
Javascript
const express = require( "express" );
const router = express.Router();
const {
getRestaurantsByFilters,
} = require( "../controllers/restaurant.controller" );
router.route( "/restaurants" ).post(getRestaurantsByFilters);
module.exports = router;
|
Javascript
const RestaurantModel = require( "../models/Restaurant.model" );
const getRestaurantsByFilters = async (req, res) => {
console.log( "request body is" , req.body);
try {
const { location, rating, cuisines } = req.body;
const query = {};
if (location) {
query.location = location;
}
if (rating?.length > 0) {
if (rating == 3) {
query.rating = { $gte: 3, $lte: 5 };
} else {
query.rating = { $gte: 4, $lte: 5 };
}
}
if (cuisines?.length > 0) {
query.cuisines = { $ in : cuisines };
}
const restaurants = await RestaurantModel.find(query);
if (restaurants.length > 0) {
res.json({ success: true , data: restaurants });
} else {
res.json({ success: false , message: "No such data found!" });
}
} catch (error) {
res.json({ success: false , error: error.message });
}
};
module.exports = { getAllRestaurants, getRestaurantsByFilters };
|
Steps to run the application :
Step 1 : Open Restaurant-Recommendation/backend folder in new terminal
Step 2 : Use the following command to start backend server
npm start
Now the backend server starts on port 4000
Step 3 : Now move to Restaurant-Recommendation / frontend folder and in the new terminal ,use the following command to start frontend.
npm start.
Output:
Restaurant Recommendation
Share your thoughts in the comments
Please Login to comment...