sofiane-abou-abderrahim / javascript-nodejs-introduction-frontend

This frontend code is my "JavaScript Share My Place" application built with Webpack which communicate with my "JavaScript NodeJS Introduction Share My Place" application backend..

Home Page:https://sofiane-abou-abderrahim.github.io/javascript-nodejs-introduction-frontend/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

google is not defined

sofiane-abou-abderrahim opened this issue · comments

I built a NodeJS backend application and a Webpack frontend application.
I use MongoDB as a database and the Google Maps API.

My goal is to deploy my backend code to Heroku and my frontend code to GitHub Pages.

Before that, I wanted to protect my sensitive data from my both apps using .env files and exclude them in .gitignore.
For that, I had to install some webpack packages and dotenv to handle the access to my environment variables.
Then, I tweaked my code in several files, especially webpack.config.js.

After that, I wanted to try my apps in development mode to see if they work.

As a matter of fact, I have an issue with the Google Maps API.

Indeed, I can get a location with the API and create a sharable link.
However, when I paste that copied link to a new page, I get an error message alert saying 'google is not defined'.

Here some of my frontend code:

Location.js:

const googleMapsApiKey = process.env.GOOGLE_MAPS_API_KEY;

// Function to initialize the Google Maps API
function loadGoogleMapsAPI() {
  const script = document.createElement('script');
  script.src = `https://maps.googleapis.com/maps/api/js?key=${googleMapsApiKey}&callback=Function.prototype`;
  script.defer = true;
  script.async = true;
  document.head.appendChild(script);
  // console.log(googleMapsApiKey);
}

// Call the loadGoogleMapsAPI function to start loading the Google Maps API
loadGoogleMapsAPI();

export async function getAddressFromCoords(coords) {
  const response = await fetch(
    `https://maps.googleapis.com/maps/api/geocode/json?latlng=${coords.lat},${coords.lng}&key=${googleMapsApiKey}`
  );
  if (!response.ok) {
    throw new Error('Failed to fetch address. Please try again!');
  }
  const data = await response.json(); // extracts the response data
  if (data.error_message) {
    throw new Error(data.error_message);
  } // it could also fail without using an error status code, so without making it to the if (!response.ok) {} (it's a Google specific thing)

  // console.log(data);
  const address = data.results[0].formatted_address;
  return address; // since we're using async/await, this is in the end what this invisibly created promise will resolve to
}

export async function getCoordsFromAddress(address) {
  const urlAddress = encodeURI(address); // translated into a URL-friendly encoding: we get a URL-friendly string back
  // we could use axios here or then() and catch() as well
  const response = await fetch(
    `https://maps.googleapis.com/maps/api/geocode/json?address=${urlAddress}&key=${googleMapsApiKey}`
  );
  if (!response.ok) {
    throw new Error('Failed to fetch coordinates. Please try again!');
  }
  const data = await response.json(); // extracts the response data
  if (data.error_message) {
    throw new Error(data.error_message);
  } // it could also fail without using an error status code, so without making it to the if (!response.ok) {} (it's a Google specific thing)

  // console.log(data);
  const coordinates = data.results[0].geometry.location;
  return coordinates;
}

Map.js:

export class Map {
  constructor(coords) {
    // this.coordinates = coords;
    this.render(coords);
  }

  render(coordinates) {
    if (!google) {
      alert('Could not load maps library - please try again later!');
      return;
    }

    // if (typeof google === 'undefined') {
    //   alert('Could not load maps library - please try again later!');
    //   return;
    // }

    const map = new google.maps.Map(document.getElementById('map'), {
      center: coordinates,
      zoom: 16
    });

    new google.maps.Marker({ position: coordinates, map: map });
  }
}

I shared this 2 files because I think the problem comes from them.

Location.js is responsible of getting the coordinates.
And Map.js is responsible of setting up Google Maps parameters.

I think the issue comes from Location.js because it seems like I should use a callback funtion that would be linked to Map.js.

Anyone has a solution?

I fixed the issue. I wanted to protect my sensitive data from frontend and backend. So, I stored it in .env files as variables and tried to access it in my code.

But, a quick solution was to create a key.js file for example, where I stored my Google Maps API key in a key constant.

export const key = 'my-api-key';

Then, I imported this key in the files that use this API key, which are:

MyPlace.js and SharePlace.js:

import { key } from '../key';

document.querySelector('script').src =`https://maps.googleapis.com/maps/api/js?key=${key}&callback=Function.prototype`;
Location.js:
import { key } from '../../key';

export async function getAddressFromCoords(coords) {
    const response = await fetch(
      `https://maps.googleapis.com/maps/api/geocode/json? 
       latlng=${coords.lat},${coords.lng}&key=${key}`
    );
    ...
}

export async function getCoordsFromAddress(address) {
    const urlAddress = encodeURI(address);
    const response = await fetch(
      `https://maps.googleapis.com/maps/api/geocode/json? 
       address=${urlAddress}&key=${key}`
    );
    ...
}

Then, in my HTML code, I removed the src attribute and left <script defer></script>:

dist/index.html:

<link rel="stylesheet" href="assets/styles/app.css" />
<link rel="stylesheet" href="assets/styles/share-place.css" />
<script defer></script>
<script src="assets/scripts/SharePlace.js" defer></script>

dist/my-place/index.html:

<link rel="stylesheet" href="../assets/styles/app.css" />
<link rel="stylesheet" href="../assets/styles/my-place.css" />
<script defer></script>
<script src="../assets/scripts/MyPlace.js" defer></script>

For the backend, I protect my sensitive data with environment variables set up in Heroku.

Now, my sensitive data are protected. However, it is still possible to see the frontend data when I push it online with GitHub Pages for example. And I don't think there is a way to change this.