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)