DEV Community

Cover image for Integrating dotenv and Google Maps API into your Angular project
Artem Artemev
Artem Artemev

Posted on • Updated on

Integrating dotenv and Google Maps API into your Angular project

When you store your Angular project in a public git repo, oftentimes you don’t want to expose your API keys, such as Google Maps API key, to everyone. Although that doesn’t bring you any security issues, as the keys can be seen by everyone through the dev tools anyway, there are the reasons you still don’t want to store them in the repo. For example, when people want to use their API keys instead, they will have to search through your code to replace the keys. But with the keys stored in the .env file, they can just copy the .env.example file and insert the new values.

Files in the environment folder do not fit this purpose, as they are usually tracked by Git. So to hide our keys and make them easy to replace, we can use the magic of the .env file.

Table of contents

Prerequisites

In this example, we are going to integrate the Google Maps API and store the API keys in the .env file.

Firstly, load the necessary npm packages.

npm i -D @angular-builders/custom-webpack @types/node @types/googlemaps dotenv-webpack

Install the official Google Maps API component for Angular:

npm i @angular/google-maps

dotenv integration

Create webpack configs for development and production environments:

/webpack-dev.config.js

const Dotenv = require("dotenv-webpack");

module.exports = {
  plugins: [
    new Dotenv(),
  ],
};
Enter fullscreen mode Exit fullscreen mode

Set the systemvars key if you plan to use Netlify environment variables in production. You can see all the available options on dotenv-webpack npm page.

/webpack-prod.config.js

const Dotenv = require("dotenv-webpack");

module.exports = {
  plugins: [
    new Dotenv({
      systemvars: true,
    }),
  ],
};
Enter fullscreen mode Exit fullscreen mode

Then open angular.json file and modify the build target as follows:

/angular.json

"architect": {
    "build": {
      "builder": "@angular-builders/custom-webpack:browser",
      "options": {
        "customWebpackConfig": {
            "path": "./webpack-dev.config.js"
            },
...
      "configurations": {
        "production": {
            "customWebpackConfig": {
                "path": "webpack-prod.config.js"
            },
          "fileReplacements": [
...
Enter fullscreen mode Exit fullscreen mode

Modify the serve target so the values from .env file were also accesible during the development process with the serve command:

/angular.json

"serve": {
    "builder": "@angular-builders/custom-webpack:dev-server",
},
Enter fullscreen mode Exit fullscreen mode

Open tsconfig.app.json and the types for nodejs:

/tsconfig.app.json

"compilerOptions": {
    "types": ["node"]
},
Enter fullscreen mode Exit fullscreen mode

Now create .env.example and .env. Don't forget to add .env to .gitignore file

/.env.example

# Google maps key
GOOGLE_MAPS_KEY=
Enter fullscreen mode Exit fullscreen mode

Insert your actual Google Maps key into .env file.

/.env

GOOGLE_MAPS_KEY=your key
Enter fullscreen mode Exit fullscreen mode

/.gitignore

/.env
Enter fullscreen mode Exit fullscreen mode

Duplicate the variables into the environment files.

/src/environments/environment.ts

export const environment = {
  production: false,
  googleMapsKey: process.env.GOOGLE_MAPS_KEY,
};
Enter fullscreen mode Exit fullscreen mode

/src/environments/environment.prod.ts

export const environment = {
  production: true,
  googleMapsKey: process.env.GOOGLE_MAPS_KEY,
};
Enter fullscreen mode Exit fullscreen mode

Integrating Google Maps API

Note that the method below with jsonp only makes sense if you have a component with the map constantly residing on your page i.e. in the footer. If you have it on a dedicated page please note that google maps script will be loaded each time the page is loaded!

Modify your app.module.ts (or any module where you declare the component that will use Google Maps) as follows:

/src/app/app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { GoogleMapsModule } from '@angular/google-maps';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

import { HttpClientJsonpModule, HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    AppRoutingModule,

   // Import http and jsonp modules
   // as well as the google maps module
    HttpClientModule,
    HttpClientJsonpModule,
    GoogleMapsModule,
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

The component which will use Google Maps will look like this:

/src/app/app.component.html

import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { Observable, of } from 'rxjs';
import { environment } from 'src/environments/environment';
import { map, catchError } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent implements OnInit {
  // Some maps options
  mapOptions: google.maps.MapOptions = {
    zoom: 15,
    center: {
      lat: 35.3606255,
      lng: 138.7273634,
    },
    mapTypeId: 'hybrid',
  };
  mapMarker: google.maps.MarkerOptions = {
    position: {
      lat: 35.3606255,
      lng: 138.7273634,
    },
    label: {
      color: 'red',
      text: 'Yep',
    },
    title: 'Yep',
  };

  // This observable will emit the value when the Maps API is loaded
  // This is needed to make sure we don't access the API when it's not loaded yet
  mapsLoaded: Observable<boolean>;

  constructor(httpClient: HttpClient) {
    this.mapsLoaded = httpClient
      .jsonp(
        `https://maps.googleapis.com/maps/api/js?key=${environment.googleMapsKey}`,
        'callback'
      )
      .pipe(
        map(() => true),
        catchError(() => of(false))
      );
  }

  ngOnInit() {}
}
Enter fullscreen mode Exit fullscreen mode

And the template file will look like this:

/src/app/app.component.html

<div *ngIf="mapsLoaded | async">
    <google-map [zoom]="mapOptions.zoom"
        [center]="mapOptions.center"
        [mapTypeId]="mapOptions.mapTypeId">
        <map-marker [position]="mapMarker.position"
            [label]="mapMarker.label"
            [title]="mapMarker.title">
        </map-marker>
    </google-map>
</div>
Enter fullscreen mode Exit fullscreen mode

Using multiple environments

Find instructions below if you want to have separate configs for development, production, and staging environments.

Firstly, create a file for each of your environments accordingly, i.e. .env.production, .env.development and .env.staging.

Now you can put the commands into package.json that will look something like this:

/package.json

  "scripts": {
    "start:prod": "APP_ENV=production ng serve",
    "start:dev": "APP_ENV=development ng serve",
    "start:staging": "APP_ENV=staging ng serve",

    "build:prod": "APP_ENV=production ng build --prod",
    "build:dev": "APP_ENV=development ng build --prod",
    "build:staging": "APP_ENV=staging ng build --prod"
  },
Enter fullscreen mode Exit fullscreen mode

Now you can just write function that will check the process.env.APP_ENV in webpack config file and return the right config:

/webpack-dev.config.js

const Dotenv = require("dotenv-webpack");

function getConfig() {
  if (process.env.APP_ENV === 'production') {
    return '.env.production'
  } else if (process.env.APP_ENV === 'development') {
    return '.env.development'
  } else {
    return '.env.staging'
  }
}

module.exports = {
  plugins: [
    new Dotenv({
      path: getConfig()
    }),
  ],
};
Enter fullscreen mode Exit fullscreen mode

Using Netlify environment variables

Because we do not store .env files in git repositories, Netlify cannot read them and emit them into the process.env variables. Fortunately, we have Netlify environment variables which will be emitted into process.env during the build time of your application.

You can set the environment variables in Build & deploy section of your site settings.

Alt Text

The important thing is to not forget to include the systemvars key into your dotenv config so that can work.

const Dotenv = require("dotenv-webpack");

module.exports = {
  plugins: [
    new Dotenv({
      systemvars: true,
    }),
  ],
};
Enter fullscreen mode Exit fullscreen mode

Top comments (0)