Skip to main content

Angular Environments

Introduction

When developing an Angular application, you'll often need to work with different environments - development, testing, staging, production, and possibly others. Each environment might require different configuration settings, such as API endpoints, feature flags, or external service credentials.

Angular provides a built-in solution called "Environments" that allows you to define environment-specific configuration and switch between them during development and deployment phases. This feature helps you maintain clean, maintainable code without hardcoding environment-specific values throughout your application.

In this guide, we'll explore how Angular environments work, how to configure them, and how to use them effectively in your projects.

Understanding Angular Environments

Angular environments are configuration files that contain environment-specific variables. By default, Angular CLI sets up two environments for your project:

  1. Development: Used during local development
  2. Production: Used when building for deployment

These environments are defined as TypeScript files in the src/environments/ directory of your Angular project.

How Environments Are Structured

Let's examine the default environment files in a typical Angular project:

Default Environment Files

When you create a new Angular project using the Angular CLI, it automatically generates the following files:

typescript
// src/environments/environment.ts
export const environment = {
production: false,
apiUrl: 'http://localhost:3000/api'
};
typescript
// src/environments/environment.prod.ts
export const environment = {
production: true,
apiUrl: 'https://api.example.com/api'
};

The base environment.ts is used during development, while environment.prod.ts is used when building for production.

Using Environment Variables in Your Code

To use these environment variables in your Angular application, you need to import the environment file:

typescript
import { Component, OnInit } from '@angular/core';
import { environment } from '../environments/environment';

@Component({
selector: 'app-data-service',
template: '<div>{{environmentName}}</div>'
})
export class DataServiceComponent implements OnInit {
environmentName: string;
apiUrl: string;

constructor() {
this.environmentName = environment.production ? 'Production' : 'Development';
this.apiUrl = environment.apiUrl;

console.log(`Running in ${this.environmentName} mode`);
console.log(`API URL: ${this.apiUrl}`);
}

ngOnInit() {
// Make API calls using environment.apiUrl
}
}

When you run your application in development mode, the component will log:

Running in Development mode
API URL: http://localhost:3000/api

When built for production, it will log:

Running in Production mode
API URL: https://api.example.com/api

Creating Custom Environments

Angular CLI allows you to create additional environments beyond the default development and production ones. This is particularly useful for staging, testing, or QA environments.

1. Create a new environment file

First, create a new environment file in the src/environments/ directory:

typescript
// src/environments/environment.staging.ts
export const environment = {
production: false,
staging: true,
apiUrl: 'https://staging-api.example.com/api',
featureFlags: {
newUserDashboard: true,
experimentalFeature: true
}
};

2. Configure the Angular CLI to use the new environment

You need to modify your angular.json file to add configuration for your new environment:

json
{
"projects": {
"my-app": {
"architect": {
"build": {
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
},
"staging": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.staging.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": true,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
}
}
},
"serve": {
"configurations": {
"production": {
"browserTarget": "my-app:build:production"
},
"staging": {
"browserTarget": "my-app:build:staging"
}
}
}
}
}
}
}

3. Build or serve your application with the new environment

Now you can build or serve your application using the new environment configuration:

bash
# Build for staging
ng build --configuration=staging

# Serve with staging configuration
ng serve --configuration=staging

Real-World Example: API Configuration Service

Let's create a practical example of using Angular environments in a service that manages API connections:

typescript
// src/app/services/api-config.service.ts
import { Injectable } from '@angular/core';
import { environment } from '../../environments/environment';

@Injectable({
providedIn: 'root'
})
export class ApiConfigService {
private apiUrl = environment.apiUrl;
private isProduction = environment.production;

constructor() { }

getApiUrl(): string {
return this.apiUrl;
}

getHeaders() {
const headers = {
'Content-Type': 'application/json'
};

// Add additional headers for non-production environments
if (!this.isProduction) {
headers['X-Environment'] = 'Development';
headers['X-Debug-Mode'] = 'true';
}

return headers;
}

logApiCall(endpoint: string, data: any) {
// Only log API calls in non-production environments
if (!this.isProduction) {
console.log(`API call to ${endpoint}`, data);
}
}
}

Using this service in a component:

typescript
// src/app/components/user-profile/user-profile.component.ts
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { ApiConfigService } from '../../services/api-config.service';

@Component({
selector: 'app-user-profile',
templateUrl: './user-profile.component.html'
})
export class UserProfileComponent implements OnInit {
user: any;

constructor(
private http: HttpClient,
private apiConfig: ApiConfigService
) { }

ngOnInit() {
this.loadUserProfile();
}

loadUserProfile() {
const endpoint = '/users/profile';
const apiUrl = this.apiConfig.getApiUrl() + endpoint;
const headers = this.apiConfig.getHeaders();

this.apiConfig.logApiCall(endpoint, { headers });

this.http.get(apiUrl, { headers }).subscribe(
(response) => {
this.user = response;
},
(error) => {
console.error('Failed to load user profile', error);
}
);
}
}

Adding Environment Variables at Build Time

Sometimes you may need to set environment variables at build time rather than having them hardcoded in your environment files. This is especially useful when deploying to different environments using CI/CD pipelines.

Using Angular CLI with Environment Variables

You can create a script to replace environment variables at build time:

typescript
// set-env.ts
const fs = require('fs');
const dotenv = require('dotenv');

// Load environment variables from .env file
const environment = process.env.NODE_ENV || 'development';
const result = dotenv.config({
path: `.env.${environment}`
});

if (result.error) {
throw result.error;
}

// Create the environment file content
const environmentFileContent = `
export const environment = {
production: ${process.env.PRODUCTION === 'true'},
apiUrl: '${process.env.API_URL}',
version: '${process.env.VERSION}'
};
`;

// Write the content to the appropriate file
const targetPath = './src/environments/environment.ts';
fs.writeFile(targetPath, environmentFileContent, (err) => {
if (err) {
console.error(err);
throw err;
}
console.log(`Environment file generated at ${targetPath}`);
});

Then update your package.json to include a script:

json
{
"scripts": {
"config": "ts-node set-env.ts",
"start": "npm run config && ng serve",
"build:prod": "NODE_ENV=production npm run config && ng build --configuration=production"
}
}

Best Practices for Angular Environments

  1. Don't store sensitive information in your environment files - they are compiled into your JavaScript bundle and are visible to users. For sensitive data like API keys, consider using server-side authentication or a backend proxy.

  2. Keep environment files in version control but use environment-specific values that are not sensitive. For secrets, use CI/CD environment variables.

  3. Create a sane default development environment that works out of the box for new developers.

  4. Document your environment variables so that other developers understand what each configuration option does.

  5. Group related configuration into logical objects for easier maintenance:

typescript
// src/environments/environment.ts
export const environment = {
production: false,
api: {
url: 'http://localhost:3000/api',
timeout: 30000,
retryAttempts: 3
},
features: {
enableNewDashboard: true,
enableExperiments: true
},
analytics: {
enabled: false,
trackingId: 'UA-DEVELOPMENT'
}
};

Summary

Angular environments provide a clean, maintainable way to manage configuration across different deployment contexts. They allow you to:

  • Define environment-specific variables and settings
  • Switch between environments during development and build processes
  • Keep your code clean by avoiding hardcoded configuration values
  • Add custom environments beyond the default development and production options

By effectively using environments, you can build Angular applications that are easy to deploy to multiple environments while maintaining clean, maintainable code.

Additional Resources

Exercises

  1. Create a new environment called "testing" that includes configuration for connecting to test databases and APIs.

  2. Implement a feature flag service that reads flags from the environment configuration to enable/disable features in your application.

  3. Modify an existing Angular application to use different analytics tracking IDs based on the environment.

  4. Create a build script that dynamically generates environment files based on command line arguments or environment variables.

  5. Implement a service that displays an environment badge (e.g., "DEV", "STAGING", "PROD") in the corner of your application, but only in non-production environments.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)