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
- dotenv integration
- Google Maps API integration
- Using multiple environments
- Using Netlify environment variables
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(),
],
};
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,
}),
],
};
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": [
...
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",
},
Open tsconfig.app.json and the types for nodejs:
/tsconfig.app.json
"compilerOptions": {
"types": ["node"]
},
Now create .env.example and .env. Don't forget to add .env to .gitignore file
/.env.example
# Google maps key
GOOGLE_MAPS_KEY=
Insert your actual Google Maps key into .env file.
/.env
GOOGLE_MAPS_KEY=your key
/.gitignore
/.env
Duplicate the variables into the environment files.
/src/environments/environment.ts
export const environment = {
production: false,
googleMapsKey: process.env.GOOGLE_MAPS_KEY,
};
/src/environments/environment.prod.ts
export const environment = {
production: true,
googleMapsKey: process.env.GOOGLE_MAPS_KEY,
};
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 {}
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() {}
}
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>
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"
},
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()
}),
],
};
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.
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,
}),
],
};

Top comments (0)