How do I set the baseUrl for Angular HttpClient?
Asked Answered
H

7

92

I did not find a way in the documentation to set the base API URL for HTTP requests. Is it possible to do this with the Angular HttpClient?

Hookworm answered 17/8, 2017 at 12:52 Comment(5)
You could create an interceptor that will update the url with whatever base you want.Lied
isn't it an overhead to use interceptors for this case?Hookworm
If you want to change only this yes it might be. Another option would be to have a simple function getApiUrl() that will make any transformations required, like adding the base path.Lied
Or create a class derived from XHRBackend and create the connection with the base url in place. There are pros and cons for each approach, I'm not aware of any 'simpler' way of providing this.Lied
I personally use an interceptor but recommend to add other functionalities to it as well such as setting headers for all requests.Zerline
S
128

Use the new HttpClient Interceptor.

Create a proper injectable that 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 apiReq = req.clone({ url: `your-api-url/${req.url}` });
    return next.handle(apiReq);
  }
}

The HttpInterceptor can clone the request and change it as you wish, in this case I defined a default path for all of the http requests.

Provide the HttpClientModule with the following configurations:

providers: [{
      provide: HTTP_INTERCEPTORS,
      useClass: APIInterceptor,
      multi: true,
    }
  ]

Now all your requests will start with your-api-url/

Stunner answered 17/8, 2017 at 13:2 Comment(5)
This is great! One thing though, you do not need to use Injectable decorator here, it is only required when you want ot inject something into decorated class.Hallee
And if you want different HttpClients to use different base urls how do you manage that? Some sort of marker in the request that the injectable switches on, multiple injectors. Doesn't seem at all elegant to me.Kerley
I cry when I see this code. Why cant we just create a client instance that has a baseUrl property instead of this middleware-like solution that is over the top for such a simple use caseFlanch
Am I the only one to see an overhead on req.clone by doing it for each request? HttpRequest seems to be immutable, so on big request, I guess there would add a significant overhead processing? I understand it looks neat, but isn't it adding a useless reprocessing? Am I missing something? FYI: I am a front noobMalnutrition
The problem with this is that it will change ALL requests, not just the one done to the api. For example there is something like angular-svg-icon that uses the HttpClient and does not work anymore.Bushwa
C
60

Based on TheUnreal's very useful answer, the interceptor can be written to get the base url through DI:

@Injectable()
export class BaseUrlInterceptor implements HttpInterceptor {

    constructor(
        @Inject('BASE_API_URL') private baseUrl: string) {
    }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

        const apiReq = request.clone({ url: `${this.baseUrl}/${request.url}` });
        return next.handle(apiReq);
    }
}

BASE_API_URL can be provided by the application module:

providers: [
  {
    provide: HTTP_INTERCEPTORS,
    useClass: BaseUrlInterceptor,
    multi: true
  },
  {
    provide: "BASE_API_URL", useValue: environment.apiUrl
  }
]

where environment is the object automatically created by the CLI when generating the project:

export const environment = {
  production: false,
  apiUrl: "..."
}; 
Cupid answered 10/4, 2018 at 15:31 Comment(9)
where do you set BASE_API_URL?Moslemism
@emaillenin - since there is more than just one comment, I have added the information within the answer. Basically, the value is provided "globally".Cupid
@Alexei what if we need consume 2 different BASE_API_URL? I mean we have a project and it uses 2 API servers with different baseTerrazzo
@Terrazzo - you can define two properties in environment object and provide two values in application module (I assume that different components use either of them so it makes sense to inject whatever is needed in that particular component).Cupid
@Alexei the problem is that how that interceptor would be able to define which baseUrl is to use.Terrazzo
and how you call that environment and other setup docs here: angular.io/guide/buildDeodand
thats what my hero alexei is! thanks for it..exactly what I was looking forKarachi
@Alexei-checkCodidact You don't need to inject BASE_API_URL you can simply access it using environment.apiUrl, this value will change based on your current environment..Od
@Alexei-checkCodidact What is the best way to exclude requests to external api to avoid a https://api.external/localhost:44334 ?Od
C
29

Everybody who followed Alexei answer and couldn't make it work like me - it's because you also have to add to providers array this element

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

Unfortunately I have too low reputation to add a comment to his answer.

Commination answered 25/6, 2021 at 10:23 Comment(2)
I voted positively to help you, my friend.Planksheer
@BraianSilva thanks buddy, you actually are the reason I can comment now!Commination
K
7

Why not create an HttpClient subclass that has a configurable baseUrl? That way if your application needs to communicate with multiple services you can either use a different subclass for each, or create multiple instances of a single subclass each with a different configuration.

@Injectable()
export class ApiHttpClient extends HttpClient {
  public baseUrl: string;

  public constructor(handler: HttpHandler) {
    super(handler);

    // Get base url from wherever you like, or provision ApiHttpClient in your AppComponent or some other high level
    // component and set the baseUrl there.
    this.baseUrl = '/api/';
  }

  public get(url: string, options?: Object): Observable<any> {
    url = this.baseUrl + url;
    return super.get(url, options);
  }
}
Kerley answered 6/9, 2018 at 9:18 Comment(1)
and anyone else who is thinking of doing this please don't. HttpClient is a "Final" class and Angular does not recommend extending "Final" classes as its internal implementation might change. More on this here: github.com/angular/angular/blob/13.1.1/docs/…Tureen
G
5

Excerpts from Visual studio 2017 asp.net core webapi angular sample application.

include below lines in Main.ts

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

const providers = [
  { provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] }
];

in your component

  constructor(http: HttpClient, @Inject('BASE_URL') baseUrl: string) {
    http.get<WeatherForecast[]>(baseUrl + 'api/SampleData/WeatherForecasts').subscribe(result => {
      this.forecasts = result;
    }, error => console.error(error));
  }

my complete main.ts code looks like below

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

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

const providers = [
  { provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] }
];

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic()
  .bootstrapModule(AppModule)
  .catch(err => console.error(err));

my component code looks like below

import { Component, Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'fetch-weather',
  templateUrl: './weather.component.html',
  styleUrls: ['./weather.component.scss']
})

export class WeatherComponent {
  public forecasts: WeatherForecast[];

  constructor(http: HttpClient, @Inject('BASE_URL') baseUrl: string) {
    http.get<WeatherForecast[]>(baseUrl + 'api/SampleData/WeatherForecasts').subscribe(result => {
      this.forecasts = result;
    }, error => console.error(error));
  }
}

interface WeatherForecast {
  dateFormatted: string;
  temperatureC: number;
  temperatureF: number;
  summary: string;
}

Gunplay answered 13/3, 2019 at 13:50 Comment(1)
This is a pretty great solution. Especially because it can use the --base-href functionality that angular already inlcudes - makes it so easy to host api + angular app in a subfolder.Trio
G
4

you don't necessarily need a base URL with HttpClient, the docs says you just have to specify the api part of the request, if you are making calls to the same server it is straightforward like this:

this.http.get('/api/items').subscribe(data => {...

However, you can if you need or want to, specify a base URL.

I have 2 suggestions for doing that:

1. A helper class with a static class property.

export class HttpClientHelper {

    static baseURL: string = 'http://localhost:8080/myApp';
}


this.http.get(`${HttpClientHelper.baseURL}/api/items`); //in your service class

2. A base class with a class property so any new service should extend it:

export class BackendBaseService {

  baseURL: string = 'http://localhost:8080/myApp';

  constructor(){}
}

@Injectable()
export class ItemsService extends BackendBaseService {

  constructor(private http: HttpClient){  
    super();
  }
      
  public listAll(): Observable<any>{    
    return this.http.get(`${this.baseURL}/api/items`);
  }

}
Grandfather answered 15/11, 2017 at 16:31 Comment(0)
L
2

I think there is no default way to do this. Do the HttpService and inside you can define property of your default URL, and make methods to call http.get and others with your property URL. Then inject HttpService instead of HttpClient

Ligni answered 17/8, 2017 at 12:55 Comment(1)
I did it for Http service, but hoped that HttpClient is a bit smarterHookworm

© 2022 - 2024 — McMap. All rights reserved.