Environment Config & Multi-provider Tokens
Manage environment-specific settings and provide multiple services for the same token.
Every app has different settings for different environments โ development, staging, production. You need different API URLs, feature flags, and debugging tools for each.
// Development: http://localhost:4200/api
// Staging: https://staging-api.hyderabad.com
// Production: https://api.hyderabad.com
Environment configuration keeps these differences organized and prevents accidentally using production API keys in development.
The traditional approach uses separate files for each environment with angular.json handling the swap during build.
// src/environments/environment.ts (dev)
export const environment = {
production: false,
apiUrl: 'http://localhost:4200/api',
enableDebug: true
};
// src/environments/environment.prod.ts (prod)
export const environment = {
production: true,
apiUrl: 'https://api.hyderabad.com',
enableDebug: false
};
Problem: Easy to accidentally import the wrong file. Not tree-shakeable โ unused config stays in bundle.
Modern Angular (17+) uses app.config.ts with InjectionToken-based configuration. No file swapping needed.
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { API_URL, APP_CONFIG, IS_PRODUCTION } from './config/app-config';
function getApiUrl(): string {
return window.location.hostname === 'localhost'
? 'http://localhost:4200/api'
: 'https://api.hyderabad.com';
}
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(),
{ provide: API_URL, useFactory: getApiUrl },
{ provide: IS_PRODUCTION, useValue: !getApiUrl().includes('localhost') },
]
};
Benefits: Single file, type-safe, tree-shakeable, no file replacement config needed. The factory function determines the value at runtime based on the hostname.
Some Angular features need MULTIPLE providers for the same token. For example, HTTP_INTERCEPTORS โ you can have multiple interceptors, and Angular runs them in order.
// New way โ functional interceptors
provideHttpClient(
withInterceptors([authInterceptor, loggingInterceptor, errorInterceptor])
);
// Old way โ class-based with multi: true
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true },
]
What multi: true does: Instead of one provider replacing the previous one, Angular collects ALL providers with multi: true into an array. inject(HTTP_INTERCEPTORS) returns [AuthInterceptor, LoggingInterceptor, ErrorInterceptor].
Common multi tokens: HTTP_INTERCEPTORS, APP_INITIALIZER, LOCALE_ID.
Follow these practices for clean, maintainable configuration:
1. Use InjectionToken for config, not environment objects
// GOOD
const API_URL = new InjectionToken('api-url');
// AVOID โ old pattern
import { environment } from './environments/environment';
2. Group related providers โ keep app.config.ts clean by grouping config providers into a single function.
3. Secrets NEVER in frontend config โ API keys and passwords in environment.ts are visible in the browser. Use a backend proxy.
4. Feature flags as InjectionToken โ type-safe feature flags with InjectionToken<Record<string, boolean>>.
5. Keep config files focused โ one config file per domain (api, auth, features).
Key Takeaways
- โ Environment config manages different settings for dev/staging/production
- โ Old way: environment.ts files with angular.json fileReplacements
- โ New way: InjectionToken + useFactory in app.config.ts โ no file swapping
- โ multi: true collects multiple providers for the same token into an array
- โ Never put secrets in frontend config โ always use a backend proxy
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login