Open In App

Restaurant Recommendation using MERN

Last Updated : 15 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

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.

restro

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

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-screenshot

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




// App.js
 
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




// NavBar.js
 
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




// Card.js
 
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
            .post("http://localhost:4000/api/restaurants", {
                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




// useContext.js
 
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




// useRestaurant.js
 
import { useContext } from "react";
import RestaurantContext from "../Context/useContext";
 
const useRestaurantContext = () => {
    return useContext(RestaurantContext);
};
 
export default useRestaurantContext;


CSS




/* App.module.css */
 
.headers {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-left: 5vw;
    margin-right: 5vw;
    margin-top: 3vh;
    padding-bottom: 2vh;
    border-bottom: 1px solid gray;
}
 
.locationContainer {
    width: 10vw;
}
 
.cuisinesContainer {
    margin-top: 5vh;
    width: 15vw;
    height: 10vh;
    overflow-y: scroll;
}
 
.ratingContainer {
    width: 10vw;
}
 
.restaurants {
    padding-bottom: 3vh;
}
 
.restaurants h3 {
    margin-left: 5vw;
    margin-top: 1vh;
    margin-bottom: 1vh;
}


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-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




// index.js
 
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
 
const cors = require("cors");
 
require("dotenv").config();
 
// create a express app
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
 
// Connect to mongodb using mongoose
 
// Please use process.env.MONGO_URL instead of my mongodb url
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");
});
 
// Start server
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




// Restaurant.model.js
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




//restaurant.route.js
 
const express = require("express");
const router = express.Router();
 
const {
    getRestaurantsByFilters,
} = require("../controllers/restaurant.controller");
 
router.route("/restaurants").post(getRestaurantsByFilters);
 
module.exports = router;


Javascript




// restaurant.controller.js
 
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:

restro

Restaurant Recommendation



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads