Creating a Restaurant Finder Application Using ReactJS and Amplication

Saurav Jain
Saurav Jain
Jan 15, 2024
Creating a Restaurant Finder Application Using ReactJS and AmplicationCreating a Restaurant Finder Application Using ReactJS and Amplication

Introduction

This tutorial will guide you through building a restaurant finder application. This full-stack web application will allow users to search for restaurants based on location (zip code) and view a list of restaurants that match their criteria. We'll create the backend and frontend components and demonstrate how to connect them to create a fully functional restaurant finder.


This tutorial will include how to create a backend using Amplication, create a frontend using ReactJS, and connect the backend created by Amplication to the frontend.

Creating the backend:

In this part of the tutorial, we will use Amplication to develop the backend of our restaurant finder application. Amplication creates a fully functional production-ready backend with REST and GraphQL API, authentication, databases, best practices, etc., in a few minutes.

  • Go to https://amplication.com.
  • Login into it or make an account if you have not already.
  • Create a new app and name it restaurant-finder-backend.
  • Click on Add Resource and then on Service and name it restaurant-finder.
  • Then, connect your GitHub account to the one on which you want Amplication to push the backend code and select or create a repository.
  • Choose which API you want; we will go by the default, and that is both.
  • Choose monorepo in this step.
  • Choose the database. We will go with PostgreSQL.
  • We will create entities from scratch.
  • We want to have auth in the app.
  • Click on create service, and the code is generated now.
  • Go to Entities and click on add entity:
    • Entity Name: Restaurant
    • First field: name [Searchable, Required, Single Line Text]
    • Second field: address [Searchable, Required, Single Line Text]
    • Third field: phone [Searchable, Required, Single Line Text]
    • Fourth field: zipCode [Searchable, Required, Single Line Text]

Click on Commit Changes and Build, and in a few minutes, the code will be pushed to the GitHub repository. Go to the repo and approve the Pull Request that Amplication created.

Now, you have all the backend code generated by Amplication in your GitHub repository. It will look like this:


Backend Code: https://github.com/souravjain540/restaurant-finder

Now clone your repository, open it in the IDE of your choice, go to restaurant-finder-backend/apps/restaurant-finder, and follow these commands:

npm install
npm run prisma:generate
npm run docker:dev
npm run db:init
npm run start

After all these steps, you will have a backend running on localhost:3000. As Amplication comes with AdminUI and Swagger Documentation, you can go to localhost:3000/api and view all the endpoints of the API that amplication generated.


Let’s click on the auth and make a POST request by clicking on try it out with credentials admin and admin.


Now click on execute, copy the accessToken, and save it for the auth purpose later.

Now, we have our backend ready and running on our local system. It is now time to move to the frontend part.

NOTE: If you have any problems using Amplication while creating your web application or in the installation, please feel free to contact the Amplication Team on our Discord channel.

Creating the frontend:

In this part, we will build the frontend of our restaurant finder application using React, a popular JavaScript library for building user interfaces. We'll create the user interface for searching restaurants by zipcode, displaying search results, and adding new restaurants.

Setting up React Project:

First, ensure your system has Node.js and npm (Node Package Manager) installed. If not, you can download and install them from the official Node.js website.

Let's create a new React project using Create React App, a popular tool for setting up React applications with a predefined project structure. Open your terminal and run the following command:

npx create-react-app restaurant-finder

This command will create a new directory called restaurant-finder containing all the necessary files and folders for your React project.

Designing the User Interface with Components

In React, you build user interfaces by creating components. Let's design the components for our restaurant finder application.

There will be three main components in our project:

  1. SearchForm Component: This will be the main page of our project where the user will enter the zipCode, and all the restaurants with that zipCode will be shown as a list in the result. It will work in the root directory. (/)

  2. RestaurantForm Component: This page will be responsible for adding any new restaurant to the list of restaurants. It will be a form with all the relevant details. It will work at /restaurants/add.

  3. RestaurantList Component: This page will show all the restaurants available in our database. It will work at /restaurants.

Directory Structure:

To avoid any confusion, the file structure will look like this:


Create a SearchForm Component

Inside the src folder of your project, create a new file named SearchForm.js. This component will be responsible for the restaurant search form. Let's break down the code snippet of SearchForm.js step by step:

  1. Import Statements:

    import React, { useState } from 'react';
    import axios from 'axios';
    
    • The code begins with importing the necessary modules.
    • React is imported to define React components.
    • useState is a React hook used to manage component-level state.
    • axios is imported to make HTTP requests to the backend API.
  2. getAuthToken Function:

    const getAuthToken = () => {
      // Replace this with your logic to obtain the token
      return 'ebGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....'; // A placeholder token
    };
    
    • getAuthToken is a function that should be replaced with your actual logic to obtain an authentication token.
    • In this code, it returns a placeholder token.
  3. Axios Configuration:

    const api = axios.create({
      baseURL: 'http://localhost:3000/api', // Adjust the base URL to your API endpoint
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${getAuthToken()}`, // Attach the token here
      },
    });
    
    • api is an instance of Axios configured with a base URL for the backend API.
    • It sets the Content-Type header to indicate that the request body is in JSON format.
    • It also attaches an authorization header with the token obtained from getAuthToken.
  4. SearchForm Component:

    function SearchForm({ onSearch }) {
      // ...
    }
    
    • SearchForm is a React functional component that takes a prop named onSearch.
    • Inside this component, we will create the UI for searching restaurants by zip code.
  5. Component State:

    const [zipCode, setZipCode] = useState('');
    const [restaurants, setRestaurants] = useState([]);
    
    • useState is used to define two pieces of component state: zipCode and restaurants.
    • zipCode stores the user's input for the zip code.
    • restaurants will store the search results.
  6. handleSearch Function:

    const handleSearch = () => {
      if (zipCode.trim() !== '') {
        // API request to search for restaurants based on the provided zip code
        // ...
      }
    };
    
    • handleSearch is a function that is called when the user clicks the "Search" button.
    • It first checks if the zipCode is not empty.
    • If the zipCode is not empty, it makes an API request to search for restaurants based on the provided zip code.
  7. Making the API Request:

    api.get('/restaurants', {
      params: {
        where: { zipCode }, // Send the zipCode as a query parameter
      },
    })
      .then(response => {
        // Handle the API response
        // ...
      })
      .catch(error => console.error('Error searching restaurants:', error));
    
    • Axios is used to make a GET request to the /restaurants endpoint of the backend API.
    • It includes a query parameter where with the specified zipCode.
    • If the request is successful, the response is processed in the .then block, and the search results are updated in the restaurants state.
    • If there's an error, it is caught and logged in the .catch block.
  8. Rendering the UI:

    • The component returns a JSX structure for rendering the search form, search button, and search results.
    • The search results are displayed as a list of restaurants if there are any.

That's a breakdown of the SearchForm.js code. It defines a React component for searching restaurants by zip code and requests API to retrieve restaurant data based on user input.

You can look into the final code of searchForm.js: https://github.com/souravjain540/restaurant-finder-frontend/blob/main/src/components/searchForm.js

Create a RestaurantForm Component

Now, let's create a component for adding new restaurants. Create a file named RestaurantForm.js inside the src folder. This component will allow users to input restaurant details and submit them to the backend. Let's break down the code snippet of RestaurantForm.js step by step:

  1. Import Statements:

    import React, { useState } from 'react';
    import axios from 'axios';
    
    • The code begins with importing the necessary modules.
    • React is imported to define React components.
    • useState is a React hook used to manage component-level state.
    • axios is imported to make HTTP requests to the backend API.
  2. getAuthToken Function:

    const getAuthToken = () => {
      // Replace this with your logic to obtain the token
      return 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....'; // A placeholder token
    };
    
    • getAuthToken is a function that should be replaced with your actual logic to obtain an authentication token.
    • In this code, it returns a placeholder token.
  3. Axios Configuration:

    const api = axios.create({
      baseURL: 'http://localhost:3000/api', // Adjust the base URL to your API endpoint
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${getAuthToken()}`, // Attach the token here
      },
    });
    
    • api is an instance of Axios configured with a base URL for the backend API.
    • It sets the Content-Type header to indicate that the request body is in JSON format.
    • It also attaches an authorization header with the token obtained from getAuthToken.
  4. RestaurantForm Component:

    function RestaurantForm({ onFormSubmit }) {
      // ...
    }
    
    • RestaurantForm is a React functional component that takes a prop named onFormSubmit.
    • Inside this component, we will create the UI for adding a new restaurant.
  5. Component State:

    const [name, setName] = useState('');
    const [address, setAddress] = useState('');
    const [zipCode, setZipCode] = useState('');
    const [phone, setPhone] = useState('');
    
    • useState is used to define four pieces of component state: name, address, zipCode, and phone.
    • These states will store the user's input for the restaurant's name, address, zip code, and phone number.
  6. handleFormSubmit Function:

    const handleFormSubmit = (e) => {
      e.preventDefault();
    
      const newRestaurant = {
        name,
        address,
        zipCode,
        phone,
      };
    
      api.post('/restaurants', newRestaurant)
        .then(response => {
          // Call the onFormSubmit function with the newly created restaurant
          onFormSubmit(response.data);
    
          // Clear the form input fields
          setName('');
          setAddress('');
          setZipCode('');
          setPhone('');
    
          // Refresh the page after a successful submission
          window.location.reload();
        })
        .catch(error => console.error('Error creating restaurant:', error));
    };
    
    • handleFormSubmit is a function that is called when the user submits the restaurant form.
    • It first prevents the default form submission behavior.
    • It creates a newRestaurant object with the values entered in the form fields.
    • It makes a POST request to the /restaurants endpoint of the backend API to create a new restaurant.
    • If the request is successful, it calls the onFormSubmit function with the newly created restaurant data.
    • It also clears the form input fields, and then refreshes the page to reflect the updated restaurant list.
    • If there's an error, it is caught and logged.
  7. Rendering the UI:

    • The component returns a JSX structure for rendering the restaurant form.
    • The form includes fields for entering the restaurant's name, address, zip code, and phone number.
    • When the user submits the form, the handleFormSubmit function is called.

That's a breakdown of the RestaurantForm.js code. It defines a React component for adding a new restaurant to the system, and it makes an API request to create the restaurant on form submission.

You can view the whole code here: https://github.com/souravjain540/restaurant-finder-frontend/blob/main/src/components/restaurantForm.js

Creating the restaurantLists component:

Next, create a file named RestaurantList.js inside the src folder. This component will display the list of restaurants returned by the search. Let's break down the code snippet of RestaurantList.js step by step:

  1. Import Statements:

    import React, { useState, useEffect } from 'react';
    import axios from 'axios';
    
    • The code begins with importing the necessary modules.
    • React is imported to define React components.
    • useState and useEffect are React hooks used to manage component-level state and side effects.
    • axios is imported to make HTTP requests to the backend API.
  2. getAuthToken Function:

    const getAuthToken = () => {
      // Replace this with your logic to obtain the token
      return 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....'; // A placeholder token
    };
    
    • getAuthToken is a function that should be replaced with your actual logic to obtain an authentication token.
    • In this code, it returns a placeholder token.
  3. Axios Configuration:

    const api = axios.create({
      baseURL: 'http://localhost:3000/api', // Adjust the base URL to your API endpoint
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${getAuthToken()}`, // Attach the token here
      },
    });
    
    • api is an instance of Axios configured with a base URL for the backend API.
    • It sets the Content-Type header to indicate that the request body is in JSON format.
    • It also attaches an authorization header with the token obtained from getAuthToken.
  4. RestaurantList Component:

    function RestaurantList() {
      // ...
    }
    
    • RestaurantList is a React functional component that displays a list of restaurants.
    • Inside this component, we will fetch the list of restaurants from the backend and display them.
  5. Component State:

    const [restaurants, setRestaurants] = useState([]);
    
    • useState is used to define a state variable restaurants that will store the list of restaurants fetched from the API.
  6. Fetching Restaurants:

    useEffect(() => {
      api.get('/restaurants')
        .then(response => {
          setRestaurants(response.data);
        })
        .catch(error => console.error('Error fetching restaurants:', error));
    }, []);
    
    • The useEffect hook is used to fetch the list of restaurants when the component mounts (i.e., when it first renders).
    • It makes a GET request to the /restaurants endpoint of the backend API.
    • When the response is received, it sets the restaurants state with the data.
  7. Deleting Restaurants:

    const handleDelete = (restaurantId) => {
      api.delete(`/restaurants/${restaurantId}`)
        .then(() => {
          // Filter out the deleted restaurant
          setRestaurants(restaurants.filter(restaurant => restaurant.id !== restaurantId));
        })
        .catch(error => console.error('Error deleting restaurant:', error));
    };
    
    • The handleDelete function is called when a user clicks the "Delete" button next to a restaurant.
    • It makes a DELETE request to the /restaurants/{restaurantId} endpoint of the backend API to delete the restaurant.
    • After successful deletion, it updates the restaurants state by filtering out the deleted restaurant.
  8. Rendering the UI:

    • The component returns a JSX structure for rendering the list of restaurants.
    • It maps over the restaurants array and displays each restaurant's name, address, phone number, and zip code.
    • A "Delete" button is provided for each restaurant, which triggers the handleDelete function when clicked.

That's a breakdown of the RestaurantList.js code. It defines a React component for displaying a list of restaurants fetched from the backend and provides the ability to delete restaurants.

Find the complete code snippet here: https://github.com/souravjain540/restaurant-finder-frontend/blob/main/src/components/restaurantList.js

Changing the App.js file:

Let's break down the code snippet of App.js step by step:

  1. Import Statements:

    import React from 'react';
    import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
    import RestaurantList from '/Users/sauravjain/projects/my-restaurant-app/src/components/restaurantList.js';
    import RestaurantForm from '/Users/sauravjain/projects/my-restaurant-app/src/components/restaurantForm.js';
    import SearchForm from '/Users/sauravjain/projects/my-restaurant-app/src/components/searchForm.js';
    import '/Users/sauravjain/projects/my-restaurant-app/src/index.css'; // Import the CSS file
    
    • The code begins with importing the necessary modules and components.
    • React is imported to define React components.
    • BrowserRouter, Routes, and Route are imported from react-router-dom for defining and handling routes in the application.
    • RestaurantList, RestaurantForm, and SearchForm are imported as components from their respective file paths.
    • The CSS file is imported to apply styles to the application.
  2. App Component:

    function App() {
      // ...
    }
    
    • App is a React functional component that serves as the main component for the application.
    • Inside this component, you define the routes, layout, and functionality of the app.
  3. Event Handlers:

    const handleSearch = (searchResults) => {
      // Handle the search results (e.g., update state)
      console.log('Search results:', searchResults);
    };
    const handleFormSubmit = (newRestaurantData) => {
      // Update the state with the new restaurant data
      console.log('Restaurant data: ', newRestaurantData);
    };
    
    • Two event handler functions, handleSearch and handleFormSubmit, are defined. These functions are used to handle data received from child components.
    • handleSearch is intended to handle search results data, and handleFormSubmit is intended to handle new restaurant data.
  4. Router Setup:

    return (
      <Router>
        <div className="App">
          <h1>Restaurant Finder</h1>
          
          <Routes>
            <Route path="/" element={<SearchForm onSearch={handleSearch} />} />
            <Route path="/restaurants" element={<RestaurantList />} />
            <Route path="/restaurants/add" element={<RestaurantForm onFormSubmit={handleFormSubmit} />} />
            <Route path="/restaurants/edit/:id" element={<RestaurantForm />} />
          </Routes>
        </div>
        <footer>
          {/* Footer content */}
        </footer>
      </Router>
    );
    
    • The Router component is used to wrap the entire application, enabling client-side routing.
    • Inside the router, there is a div with the class name "App" that serves as the main container for the application.
    • The h1 element displays the title "Restaurant Finder."
  5. Routes Configuration:

    • Inside the Routes component, different routes are defined using the Route component from react-router-dom.
    • The routes specify which components to render when certain URLs are accessed.
  6. Route Paths and Components:

    • / path is associated with the SearchForm component. The onSearch prop is passed to it, allowing it to handle search results.
    • /restaurants path is associated with the RestaurantList component.
    • /restaurants/add path is associated with the RestaurantForm component. The onFormSubmit prop is passed to it to handle form submissions.
    • /restaurants/edit/:id path is associated with the RestaurantForm component, presumably for editing restaurant data.
  7. Footer Section:

    • Below the Router content, there is a footer section with links to the author's Twitter profile and a mention of "Backend Powered by Amplication."

This is an overview of the App.js code, which sets up the routing and components for your restaurant finder application. It defines how different components are rendered based on the URL paths and handles events with the defined event handler functions.

Have a look at the final code snippet of the App.js file here: https://github.com/souravjain540/restaurant-finder-frontend/blob/main/src/App.js

In the end, I encourage everyone to create the frontend by themselves according to their creativity, but if you want to have it like mine, please copy the index.html and index.css file as well.

In the end, your app will look like this:

Root directory(searchForm):


restaurantLists.js:


restaurantForm.js


You can have a look at the complete frontend code here: https://github.com/souravjain540/restaurant-finder-frontend/tree/main

If you have any problem with any part of this tutorial, please feel free to contact me on my Twitter account. Thanks for giving it a read.

Automate and standardize
backend development.
Get a demo