How do you configure production API URLs in Angular?
In an Angular + ASP.NET Core application, the API base URL should not be hard-coded throughout components and services.
Instead, keep the API URL in a centralized configuration and use a different value for development and production.
For example:
Development
Angular: https://localhost:4200
API: https://localhost:7001
Production
Angular: https://www.jntech.in
API: https://api.jntech.in
1. What is a production API URL?
The production API URL is the address where your backend API is available after deployment.
During development:
https://localhost:7001/api
Production:
https://api.jntech.in/api
Your Angular code needs to call the correct URL depending on the environment.
Instead of writing this everywhere:
this.http.get(
'https://api.jntech.in/api/products'
);
use centralized configuration:
this.http.get(
`${environment.apiUrl}/products`
);
2. Angular environment configuration
A common Angular approach is to use environment files together with Angular CLI file replacements.
Create:
src/
└── environments/
├── environment.ts
└── environment.production.ts
3. Development configuration
environment.ts
export const environment = {
production: false,
apiUrl: 'https://localhost:7001/api'
};
This is used for local development.
4. Production configuration
environment.production.ts
export const environment = {
production: true,
apiUrl: 'https://api.jntech.in/api'
};
Now you have:
environment.ts
↓
https://localhost:7001/api
environment.production.ts
↓
https://api.jntech.in/api
5. Configure angular.json
Angular CLI can replace the development environment file with the production version during a production build.
Conceptually, your angular.json production configuration contains:
{
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.production.ts"
}
]
}
}
}
The exact surrounding angular.json structure depends on your Angular CLI/project configuration.
The important concept is:
ng build --configuration production
↓
Angular CLI sees fileReplacements
↓
environment.ts
↓ replaced by
environment.production.ts
6. Create an Angular API service
Suppose we have a Product API.
product.ts
export interface Product {
id: number;
name: string;
price: number;
}
product-service.ts
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from '../environments/environment';
import { Product } from './product';
@Injectable({
providedIn: 'root'
})
export class ProductService {
private http = inject(HttpClient);
private apiUrl =
`${environment.apiUrl}/products`;
getProducts() {
return this.http.get<Product[]>(
this.apiUrl
);
}
}
Notice that the service doesn't know whether it is running in development or production.
It simply uses:
environment.apiUrl
7. Development execution
Suppose:
export const environment = {
production: false,
apiUrl: 'https://localhost:7001/api'
};
Then:
`${environment.apiUrl}/products`
becomes:
https://localhost:7001/api/products
Flow:
Angular
localhost:4200
↓
ProductService
↓
environment.apiUrl
↓
https://localhost:7001/api
↓
/products
↓
https://localhost:7001/api/products
8. Production build
Run:
ng build --configuration production
With the production file replacement configured, Angular builds using:
export const environment = {
production: true,
apiUrl: 'https://api.jntech.in/api'
};
Therefore:
`${environment.apiUrl}/products`
becomes:
https://api.jntech.in/api/products
Flow:
ng build --configuration production
↓
Production configuration
↓
environment.production.ts
↓
apiUrl =
https://api.jntech.in/api
↓
Angular build
↓
dist/
9. Full Simple Sample Program
Assume ASP.NET Core provides:
GET /api/products
Angular environment.ts
export const environment = {
production: false,
apiUrl: 'https://localhost:7001/api'
};
environment.production.ts
export const environment = {
production: true,
apiUrl: 'https://api.jntech.in/api'
};
product.ts
export interface Product {
id: number;
name: string;
price: number;
}
product-service.ts
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from '../environments/environment';
import { Product } from './product';
@Injectable({
providedIn: 'root'
})
export class ProductService {
private http = inject(HttpClient);
private baseUrl =
`${environment.apiUrl}/products`;
getAll() {
return this.http.get<Product[]>(
this.baseUrl
);
}
getById(id: number) {
return this.http.get<Product>(
`${this.baseUrl}/${id}`
);
}
create(product: Product) {
return this.http.post<Product>(
this.baseUrl,
product
);
}
update(id: number, product: Product) {
return this.http.put(
`${this.baseUrl}/${id}`,
product
);
}
delete(id: number) {
return this.http.delete(
`${this.baseUrl}/${id}`
);
}
}
Now every request gets its base URL from one location.
10. What happens during development?
Run:
ng serve
The application uses the development configuration.
Requests become:
GET https://localhost:7001/api/products
GET https://localhost:7001/api/products/1
POST https://localhost:7001/api/products
PUT https://localhost:7001/api/products/1
DELETE https://localhost:7001/api/products/1
11. What happens in production?
Build:
ng build --configuration production
Requests become:
GET https://api.jntech.in/api/products
GET https://api.jntech.in/api/products/1
POST https://api.jntech.in/api/products
PUT https://api.jntech.in/api/products/1
DELETE https://api.jntech.in/api/products/1
You don't need to modify ProductService.
Only the configuration changes.
12. Multiple environments
Real projects often have more than two environments.
For example:
Development
SIT
UAT
Production
You could maintain:
environments/
├── environment.ts
├── environment.sit.ts
├── environment.uat.ts
└── environment.production.ts
Example:
Development
export const environment = {
production: false,
apiUrl: 'https://localhost:7001/api'
};
SIT
export const environment = {
production: false,
apiUrl: 'https://sit-api.jntech.in/api'
};
UAT
export const environment = {
production: false,
apiUrl: 'https://uat-api.jntech.in/api'
};
Production
export const environment = {
production: true,
apiUrl: 'https://api.jntech.in/api'
};
Then your build pipeline can select the required configuration:
ng build --configuration sit
ng build --configuration uat
ng build --configuration production
provided those configurations are defined in angular.json.
13. What if Angular and ASP.NET Core use the same domain?
This is an important alternative.
Suppose Angular is served by ASP.NET Core:
https://www.jntech.in
and the API is:
https://www.jntech.in/api
You don't necessarily need a complete absolute URL.
You can configure:
export const environment = {
production: true,
apiUrl: '/api'
};
Then:
this.http.get(
`${environment.apiUrl}/products`
);
generates:
/api/products
The browser automatically resolves it against the current origin:
https://www.jntech.in/api/products
This is often cleaner for same-origin deployments.
14. Separate deployment vs same-origin deployment
Separate Angular and API
Angular
https://www.jntech.in
↓ HTTPS
ASP.NET Core
https://api.jntech.in
Configuration:
apiUrl: 'https://api.jntech.in/api'
CORS needs to be configured appropriately because the origins differ.
Same-origin deployment
https://www.jntech.in
|
├── Angular
|
└── /api
↓
ASP.NET Core
Configuration:
apiUrl: '/api'
No cross-origin request is involved.
15. Very important: Angular environment files are NOT secret
This is one of the most important interview points.
Suppose you write:
export const environment = {
apiUrl: 'https://api.jntech.in/api',
databasePassword: 'MyPassword123',
jwtSecret: 'SuperSecretKey'
};
This is wrong.
Angular runs in the browser.
During:
ng build
these values become part of the generated frontend JavaScript.
Conceptually:
environment.production.ts
↓
ng build
↓
JavaScript bundles
↓
Browser downloads them
↓
User can inspect them
Therefore, never store secrets such as:
- database passwords
- SQL connection strings
- JWT signing secrets
- private API keys
- Azure service credentials
inside Angular environment files.
The API URL itself is not a secret, so storing this is fine:
apiUrl: 'https://api.jntech.in/api'
16. Where should secrets be stored?
Secrets belong on the server side.
For example:
Angular
|
| Public configuration
| API URL
↓
ASP.NET Core
|
| Secret configuration
↓
Azure App Settings /
Environment Variables /
Azure Key Vault
|
↓
SQL Server
For example, ASP.NET Core could retrieve a connection string from secure server-side configuration.
Angular should never receive the SQL Server password.
17. Build-time configuration limitation
There is another important architectural point.
Angular environment file replacement is generally build-time configuration.
Suppose you build:
ng build --configuration production
with:
apiUrl: 'https://api1.jntech.in/api'
That URL becomes part of the generated frontend build.
If you later want:
https://api2.jntech.in/api
you generally need another build when using this approach:
Change environment configuration
↓
ng build
↓
New dist
↓
Deploy again
This can matter when organizations want:
Build once, deploy the same artifact to SIT, UAT and Production.
In that scenario, teams often use a runtime configuration approach instead.
18. Runtime configuration approach
Instead of embedding the API URL at build time, the application can load configuration when it starts.
For example:
assets/config.json
could contain:
{
"apiUrl": "https://api.jntech.in/api"
}
Angular loads this configuration when the application starts.
Then you could deploy the same Angular build while providing different configuration per environment.
Conceptually:
Same Angular Build
|
├── SIT
│ config → sit-api
|
├── UAT
│ config → uat-api
|
└── Production
config → production-api
This is more advanced than environment file replacement, but it is useful in enterprise CI/CD pipelines.
Environment files vs runtime configuration
| Feature | Environment files | Runtime config |
|---|---|---|
| Configuration time | Build time | Runtime/startup |
| Simple | Yes | More setup |
| Different build per environment | Usually | Not necessarily |
| Build once/deploy many | Less suitable | Better suited |
| Common for smaller applications | Yes | Possible |
| Enterprise CI/CD | Used, but can require multiple builds | Often useful |
For learning and interviews, start with Angular environment configuration and understand runtime configuration as the more flexible alternative.
Key Points
- Don't hard-code API URLs throughout Angular services.
- Centralize the API base URL.
- A common approach is:
environment.ts
environment.production.ts
- Development:
apiUrl: 'https://localhost:7001/api'
- Production:
apiUrl: 'https://api.jntech.in/api'
- Use it from services:
`${environment.apiUrl}/products`
- For same-origin Angular + ASP.NET Core hosting, consider:
apiUrl: '/api'
- Angular environment values are visible to the browser and must not contain secrets.
- Environment file replacement is primarily build-time configuration.
- Runtime configuration can be useful when you want to build once and deploy the same artifact across multiple environments.
Interview Questions and Answers
1. How do you configure production API URLs in Angular?
A common approach is to keep the API base URL in Angular environment configuration and use Angular CLI build configurations/file replacements to select the production value.
export const environment = {
production: true,
apiUrl: 'https://api.example.com/api'
};
2. Why shouldn't we hard-code API URLs in services?
Because the API address changes between environments such as Development, SIT, UAT, and Production. Centralized configuration makes those changes easier to manage.
3. How do you access the API URL in a service?
this.http.get(
`${environment.apiUrl}/products`
);
4. What happens during a production build?
When configured with file replacements:
ng build --configuration production
Angular CLI uses the production configuration and replaces the development environment file with the configured production environment file.
5. Can secrets be stored in environment.production.ts?
No.
Angular code is delivered to the browser, so values bundled into it can be inspected by users.
6. Is the API URL itself a secret?
No.
For example:
https://api.example.com/api
is inherently visible to browsers making API requests.
7. What should be stored on the ASP.NET Core side?
Server-side secrets such as database connection strings, credentials, private keys, and JWT signing secrets should remain in secure backend configuration.
8. What should I use if Angular and ASP.NET Core share the same domain?
A relative URL is often convenient:
apiUrl: '/api'
Then:
this.http.get('/api/products');
uses the current origin automatically.
9. What is the disadvantage of Angular environment file replacement?
It is a build-time configuration approach. Changing the embedded API URL generally means creating a new Angular build.
10. How can you build once and deploy to SIT, UAT, and Production?
Use a runtime configuration mechanism where the Angular application loads environment-specific public configuration, such as the API base URL, when it starts.
Short interview answer
I centralize the API base URL instead of hard-coding it in Angular services. For build-time configuration, I use Angular CLI environment-specific configurations and file replacements, with the production API URL selected during
ng build --configuration production. If Angular and ASP.NET Core share the same origin, I prefer a relative URL such as/api. For build-once/deploy-many pipelines, runtime configuration is more flexible. Secrets are never stored in Angular configuration because frontend values are visible to the browser.