Building a simple Full-Stack Restaurant Finder App with React, Redux, Node.js, and Google Places API (Part 1)

Vaisakh - Jun 19 - - Dev Community

Introduction
Welcome to the first part of our tutorial series on building a full-stack "Restaurant Finder" application. In this blog post, we will guide you through the process of creating a robust backend using Node.js and Express. The backend will serve as the foundation for our application, allowing us to integrate seamlessly with the Google Places API to retrieve restaurant data based on user queries.

Our goal is to develop a RESTful API that can handle requests for nearby restaurants, provide detailed information such as ratings and distances, and interact efficiently with the frontend built in React. By the end of this tutorial, you will have a functional backend server ready to power the restaurant discovery capabilities of our application.

Let's dive into the world of backend development and set the stage for a powerful and responsive "Restaurant Finder" experience!

Part 1: Building the Backend

-> Creating a RESTful Backend with Node.js and Express

In this comprehensive tutorial, we'll walk you through creating a RESTful backend for a "Restaurant Finder" app using Node.js and Express.
You'll learn
-> how to set up a basic server,
-> integrate the Google Places API to fetch nearby restaurants,
-> build a full-featured server that processes and returns sorted restaurant data based on user location.

Perfect for beginners, this guide will provide all the steps you need to get your backend up and running.

  • Initialize a new Node.js project

mkdir restaurant-finder
cd restaurant-finder
mkdir server
npm init -y

-> Install Express, Axios, CORS, and Dotenv:

npm install express axios cors dotenv

Step 1: Setting Up a Basic Express Server

  • Create Basic Server:

-> Create a file named 'server.js' in the server folder.
Add the following code to set up a basic Express server:

// server.js
const express = require("express");
const cors = require("cors");
require("dotenv").config();

const app = express();
const port = 3001;

app.use(cors());

app.get("/", (req, res) => {
  res.send("Hello, World!");
});

app.listen(port, () => {
  console.log(`Server running on http://localhost:${port}`);
});

Enter fullscreen mode Exit fullscreen mode
  • Run the Server: -> Run node server.js in terminal to start the server. -> Open a browser and navigate to http://localhost:3001 to see "Hello, World!" message.

Step 2: Adding Google Places API

  • Obtain API Key:

-> Go to the Google Cloud Console.
Create a new project or select an existing one.
Navigate to "APIs & Services" > "Library" and enable the "Places API".
Navigate to "APIs & Services" > "Credentials" and create an API key.

  • Add API Key to .env:

-> Create a .env file in the server folder and add your API key:

REACT_APP_GOOGLE_PLACES_API_KEY=your_api_key_here

Enter fullscreen mode Exit fullscreen mode
  • Update Server to Use Google Places API: Update server.js to include a new endpoint that interacts with the Google Places API:
// server.js
const express = require("express");
const axios = require("axios");
const cors = require("cors");
require("dotenv").config();

const app = express();
const port = 3001;

app.use(cors());

app.get("/api/places", async (req, res) => {
  try {
    const { lat, lng } = req.query;
    const response = await axios.get(
      "https://maps.googleapis.com/maps/api/place/nearbysearch/json",
      {
        params: {
          location: `${lat},${lng}`,
          radius: 1500,
          type: "restaurant",
          key: process.env.REACT_APP_GOOGLE_PLACES_API_KEY,
        },
      }
    );
    res.json(response.data.results);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(port, () => {
  console.log(`Server running on http://localhost:${port}`);
});

Enter fullscreen mode Exit fullscreen mode
  • Test the Endpoint:

-> Use a tool like Postman to send a GET request to http://localhost:3001/api/places?lat=37.7749&lng=-122.4194 (replace with appropriate coordinates).
-> Verify that the response contains restaurant data from the Google Places API.

Step 3: Building the Full Functional Server

  • Calculate Distance: -> Add a function to calculate the distance between two coordinates using the Haversine formula: (The Haversine formula calculates the shortest distance between two points on a sphere using their latitudes and longitudes measured along the surface. It is important for use in navigation.)
// server.js
const haversineDistance = (coords1, coords2) => {
  function toRad(x) {
    return (x * Math.PI) / 180;
  }

  const lat1 = coords1.lat;
  const lon1 = coords1.lng;
  const lat2 = coords2.lat;
  const lon2 = coords2.lng;

  const R = 6371; // Radius of the Earth in kilometers

  const x1 = lat2 - lat1;
  const dLat = toRad(x1);
  const x2 = lon2 - lon1;
  const dLon = toRad(x2);
  const a =
    Math.sin(dLat / 2) * Math.sin(dLat / 2) +
    Math.cos(toRad(lat1)) *
      Math.cos(toRad(lat2)) *
      Math.sin(dLon / 2) *
      Math.sin(dLon / 2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

  const d = R * c;

  return d;
};

Enter fullscreen mode Exit fullscreen mode
  • Update Endpoint to Include Distance and Sort: -> Modify the /api/places endpoint to include distance calculation and sorting:
// server.js
app.get("/api/places", async (req, res) => {
  try {
    const { lat, lng } = req.query;
    const response = await axios.get(
      "https://maps.googleapis.com/maps/api/place/nearbysearch/json",
      {
        params: {
          location: `${lat},${lng}`,
          radius: 1500,
          type: "restaurant",
          key: process.env.REACT_APP_GOOGLE_PLACES_API_KEY,
        },
      }
    );

    const restaurants = response.data.results.map((restaurant) => {
      const photoUrl = restaurant.photos
        ? `https://maps.googleapis.com/maps/api/place/photo?maxwidth=400&photoreference=${restaurant.photos[0].photo_reference}&key=${process.env.REACT_APP_GOOGLE_PLACES_API_KEY}`
        : null;
      return {
        name: restaurant.name,
        vicinity: restaurant.vicinity,
        rating: restaurant.rating,
        user_ratings_total: restaurant.user_ratings_total,
        distance: haversineDistance(
          { lat: parseFloat(lat), lng: parseFloat(lng) },
          {
            lat: restaurant.geometry.location.lat,
            lng: restaurant.geometry.location.lng,
          }
        ),
        photoUrl,
        place_id: restaurant.place_id
      };
    });

    const sortedRestaurants = restaurants
      .sort((a, b) => a.distance - b.distance)
      .slice(0, 10);

    res.json(sortedRestaurants);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

Enter fullscreen mode Exit fullscreen mode
  • Test Full Functionality:

-> Test the endpoint again to verify it returns a sorted list of nearby restaurants with all required details.

Congratulations!

You have successfully built a robust backend for the "Restaurant Finder" app. Your server can now handle requests, interact with the Google Places API, and return sorted restaurant data based on the user's location.

In the next part of this series, we'll focus on creating the frontend using React and TypeScript, ensuring that users have a seamless experience when searching for restaurants.
Read the next part here .

Stay tuned!

Fun Fact😉:
This project started as a take-home assignment for a job interview 🎓. While working on it, I decided to create a tutorial to help others looking for references to build similar solutions 💪. Happy coding! 🧑‍💻

. .