← Back to Articles         
Avoid Hard-Coding API URLs in Angular

Avoid Hard-Coding API URLs in Angular

Published on 01 Oct 2026     7 min read Angular
Environment & Configuration

How Do You Avoid Hard-Coding API URLs in Angular?

1. What Does Hard-Coding an API URL Mean?

Hard-coding means directly writing the ASP.NET Core API address inside your Angular service:

private apiUrl = 'https://localhost:7001/api/products';

This is problematic because the API URL normally changes between environments:

Development → https://localhost:7001/api
UAT         → https://uat-api.jntech.in/api
Production  → https://api.jntech.in/api

A common solution is to keep the base API URL in environment-specific configuration and reference it from your services.


2. Full Simple Sample Program

Project Structure

src/
├── app/
│   ├── models/
│   │   └── product.ts
│   └── services/
│       └── product.ts
│
└── environments/
    ├── environment.ts
    └── environment.production.ts

environment.ts — Development

export const environment = {
  production: false,
  apiUrl: 'https://localhost:7001/api'
};

environment.production.ts — Production

export const environment = {
  production: true,
  apiUrl: 'https://api.jntech.in/api'
};

models/product.ts

export interface Product {
  id: number;
  name: string;
  price: number;
}

services/product.ts

import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

import { environment } from '../../environments/environment';
import { Product } from '../models/product';

@Injectable({
  providedIn: 'root'
})
export class ProductService {

  private http = inject(HttpClient);

  private apiUrl = `${environment.apiUrl}/products`;

  getAll(): Observable<Product[]> {
    return this.http.get<Product[]>(this.apiUrl);
  }

  getById(id: number): Observable<Product> {
    return this.http.get<Product>(`${this.apiUrl}/${id}`);
  }

  create(product: Product): Observable<Product> {
    return this.http.post<Product>(this.apiUrl, product);
  }

  update(id: number, product: Product): Observable<void> {
    return this.http.put<void>(
      `${this.apiUrl}/${id}`,
      product
    );
  }

  delete(id: number): Observable<void> {
    return this.http.delete<void>(
      `${this.apiUrl}/${id}`
    );
  }
}

3. What Did We Avoid?

Without environment configuration:

private apiUrl =
  'https://localhost:7001/api/products';

The service itself knows about the server address.

That's undesirable.

Instead:

private apiUrl =
  `${environment.apiUrl}/products`;

Now the responsibility is separated:

environment.ts
      ↓
Base API URL
      ↓
ProductService
      ↓
/products

4. How Does It Work?

The service imports:

import { environment }
  from '../../environments/environment';

Then:

private apiUrl =
  `${environment.apiUrl}/products`;

During development:

environment.apiUrl
        ↓
https://localhost:7001/api
        +
/products
        ↓
https://localhost:7001/api/products

In production:

environment.apiUrl
        ↓
https://api.jntech.in/api
        +
/products
        ↓
https://api.jntech.in/api/products

The ProductService itself doesn't have to change.


5. Using Multiple Services

This becomes particularly useful when your application has many services.

Product Service

private apiUrl =
  `${environment.apiUrl}/products`;

Customer Service

private apiUrl =
  `${environment.apiUrl}/customers`;

Order Service

private apiUrl =
  `${environment.apiUrl}/orders`;

All of them share:

environment.apiUrl

So instead of repeating:

https://localhost:7001/api

throughout the application, it exists in one configuration location.

                  environment.apiUrl
                         │
          ┌──────────────┼──────────────┐
          ↓              ↓              ↓
   ProductService CustomerService OrderService
          ↓              ↓              ↓
      /products      /customers      /orders

6. Different Environments

You could have:

environments/
├── environment.ts
├── environment.sit.ts
├── environment.uat.ts
└── environment.production.ts

Development

export const environment = {
  apiUrl: 'https://localhost:7001/api'
};

SIT

export const environment = {
  apiUrl: 'https://sit-api.jntech.in/api'
};

UAT

export const environment = {
  apiUrl: 'https://uat-api.jntech.in/api'
};

Production

export const environment = {
  apiUrl: 'https://api.jntech.in/api'
};

Then you can build using:

ng build --configuration sit
ng build --configuration uat
ng build --configuration production

7. Configure File Replacement

For example, angular.json can contain:

"configurations": {

  "uat": {
    "fileReplacements": [
      {
        "replace": "src/environments/environment.ts",
        "with": "src/environments/environment.uat.ts"
      }
    ]
  },

  "production": {
    "fileReplacements": [
      {
        "replace": "src/environments/environment.ts",
        "with": "src/environments/environment.production.ts"
      }
    ]
  }

}

Your services still always import:

import { environment }
  from '../../environments/environment';

Angular's build configuration handles the replacement.


8. Better Organization for Larger Applications

For a larger Angular application, you can also centralize endpoint paths.

For example:

export const environment = {
  apiUrl: 'https://localhost:7001/api'
};

Then create:

api-endpoints.ts

import { environment } from '../../environments/environment';

export const API_ENDPOINTS = {

  products: `${environment.apiUrl}/products`,

  customers: `${environment.apiUrl}/customers`,

  orders: `${environment.apiUrl}/orders`

};

Then:

import { API_ENDPOINTS } from '../config/api-endpoints';

private apiUrl = API_ENDPOINTS.products;

This produces:

environment.ts
     ↓
Base API URL
     ↓
api-endpoints.ts
     ↓
 ┌────────┬─────────┬────────┐
 ↓        ↓         ↓
Products Customers Orders

This can be useful when an application contains many endpoints.


9. Another Option: Use a Relative API URL

There is another useful deployment architecture.

Suppose Angular and ASP.NET Core are exposed through the same domain:

https://www.jntech.in

Angular:

https://www.jntech.in/

API:

https://www.jntech.in/api/products

Then Angular may use a relative URL:

private apiUrl = '/api/products';

The browser automatically calls the current host:

https://www.jntech.in/api/products

This can eliminate the need to configure the hostname in Angular.

It is particularly useful when a reverse proxy/API gateway exposes the frontend and API under the same origin.


10. Build-Time vs Runtime Configuration

Environment files solve hard-coding, but they are primarily a build-time configuration mechanism.

For example:

ng build --configuration uat

produces a build containing the UAT configuration.

ng build --configuration production

produces a build containing the production configuration.

Therefore:

Source
 ├── UAT Build  → UAT API URL
 │
 └── Prod Build → Production API URL

If your deployment requirement is:

Build Angular once
        ↓
Create one artifact
        ↓
Deploy to SIT
        ↓
Promote same artifact to UAT
        ↓
Promote same artifact to Production

then consider runtime configuration instead.

For example:

Angular starts
      ↓
Loads config.json
      ↓
Reads apiUrl
      ↓
Calls ASP.NET Core

This allows the deployment environment to supply the URL rather than compiling it into Angular.


11. Don't Confuse Configuration with Security

Moving this:

'https://api.jntech.in/api'

from a service into:

environment.apiUrl

improves configuration management.

It does not make the URL secret.

Angular executes in the browser, so users can inspect the API requests.

Therefore:

Fine in Angular

export const environment = {
  apiUrl: 'https://api.jntech.in/api'
};

Don't put in Angular

export const environment = {
  sqlPassword: 'Password123',
  jwtSigningKey: 'secret',
  privateApiKey: 'secret'
};

Secrets belong on the backend.


Key Points

  • Don't hard-code the ASP.NET Core API base URL in every Angular service.
  • Centralize the base URL in environment-specific configuration:
environment.apiUrl
  • Build endpoint URLs like:
`${environment.apiUrl}/products`
  • Different environments can have different URLs:
DEV  → localhost
SIT  → SIT API
UAT  → UAT API
PROD → Production API
  • fileReplacements can select the appropriate environment file during the build.
  • For larger applications, endpoint paths can also be centralized.
  • If Angular and the API share the same origin, relative URLs such as /api/products can simplify configuration.
  • For build once, deploy everywhere, runtime configuration is usually more appropriate than build-time environment files.
  • Never store secrets in Angular configuration.

Interview Questions and Answers

1. How do you avoid hard-coding API URLs in Angular?

Store the base API URL in centralized environment-specific configuration and reference it from Angular services.

private apiUrl =
  `${environment.apiUrl}/products`;

2. Why shouldn't API URLs be hard-coded?

Because API addresses commonly differ between Development, SIT, UAT, and Production. Hard-coding makes deployments and maintenance more difficult.


3. Where can the API base URL be stored?

For build-time configuration:

export const environment = {
  apiUrl: 'https://localhost:7001/api'
};

Then access it using:

environment.apiUrl

4. Should every Angular service contain the complete server URL?

Prefer not to repeat it.

Instead of:

'https://localhost:7001/api/products'

use:

`${environment.apiUrl}/products`

This centralizes the base URL.


5. Can Angular use relative API URLs?

Yes. If the frontend and backend are exposed through the same origin, you can use:

'/api/products'

instead of hard-coding the hostname.


6. What if Angular communicates with multiple APIs?

Define separate base URLs:

export const environment = {
  productApiUrl: 'https://product-api.example.com/api',
  orderApiUrl: 'https://order-api.example.com/api'
};

Each service can then use its corresponding base URL.


7. Are environment files the only solution?

No. Common approaches include:

  • Environment/build configuration — simple and suitable when separate builds per environment are acceptable.
  • Runtime configuration — useful when the same build artifact must be promoted across environments.
  • Relative URLs — useful when frontend and API are exposed under the same origin.

8. Give a short interview answer.

I avoid hard-coding API URLs by centralizing the API base URL in Angular configuration and referencing it from services. For environment-specific builds, I can use Angular build configurations and file replacements. If the same build must be promoted through SIT, UAT, and Production, I prefer runtime configuration or relative URLs where the deployment architecture allows it.

← Back to Articles