Recently one of my Angular projects had a requirement to have multiple instances of websites hosted on the same domain with different base URLs. For example, the application URL is http://domain.com/home and now you want to create multiple sites with different base URL:

http://domain.com/client1/home
http://domain.com/client2/home
http://domain.com/client3/home

 and so on. With the change in base URL, the API path also changed from /api/GetCall to /client1/api/GetCall. 

If we want to change the base URL, we have two problems to solve:
1. Handle Angular routing to accommodate base URL.
2. Handle Http service requests and append base URL to each request.

1. Fix Routing

You just need to put a <base> tag in the head of the index or Layout page and specify a base URL like this:

<base href="/client1/" />

 So if you had an Angular route defined like this:

{ path: 'home', component: HomeComponent }

 this route would become /client1/home if <base href="/client1/" /> is present in the head section of the page.

2. Append Base URL to HTTP requests


We have an API relative URL which starts at /api/products. In this section, we want to make sure that when we make a call to API, the base URL is appended to the API URL, so in this case, the API URL becomes /client1/api/products. 

/api/products --> /client1/api/products

There are two popular ways of achieving this, one, using HttpInterceptors and, two, using dependency injection.

Using Dependency Injection:

Register a base URL provider in the module so it is available everywhere in the application:

providers: [
    { provide: 'BASE_URL', useFactory: getBaseUrl }
]

Provide factory method which gets the base URL from <base> element: 

export function getBaseUrl() {
    return document.getElementsByTagName('base')[0].href;
}

Now you can get the base URL injected and add it to URL:

export class FetchProductsComponent {
    public forecasts: IProduct[];

    constructor(http: Http, @Inject('BASE_URL') baseUrl: string) {
        http.get(baseUrl + 'api/products').subscribe(result => {
            this.products = result.json() as IProduct[];
        }, error => console.error(error));
    }
}

 

Using HttpInterceptors:

Since HttpInterceptors were introduced in Angular 4.3, this will not work on earlier versions.

 HttpInterceptor intercepts requests made using HttpClient, if you try to make requests with old Http class, interceptor won't hit.

To create an interceptor, create an Injectable class which implements HttpInterceptor.

import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class ApiInterceptor implements HttpInterceptor {
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        
        const baseUrl = document.getElementsByTagName('base')[0].href;
        const apiReq = req.clone({ url: `${baseUrl}${req.url}` });
        return next.handle(apiReq);
    }
}

Register interceptor as a provider:

{ provide: HTTP_INTERCEPTORS, useClass: ApiInterceptor, multi: true } 

 

Now whenever you make any Http request using HttpClient, ApiInterceptor will be invoked and base URL will be added to the API URL.

When you have two options to append base URL to Http requests, which one to choose? I liked the interceptor way as you do not need to inject it in all the places where you are making Http calls, just write one interceptor and done, it will intercept all the Http calls made using HttpClient.

Have any questions or suggestions? Please drop a message in the comments section below.