53

I am trying to set base url for all my angular 2 http requests. Following is the basic set up for my application.

class HttpOptions extends BaseRequestOptions {
  url:string = "http://10.7.18.21:8080/api/";
}


bootstrap(AppComponent, [
  HTTP_PROVIDERS,
  provide(RequestOptions, {useClass: HttpOptions})
]);


export class AppComponent {
  users:Array<User>
  constructor(private http: Http) {
    http.get("/users")
      .subscribe(res => this.users = res.json());
  }
}

The request is not sent to http://10.7.18.21:8080/api/users as I expected from my configuration. Instead the request is sent to http://localhost:8000/users.

How do I set base url for http request in angular 2 application?

I am using Angular 2.0.0-beta.0.

Stefan Falk
  • 23,898
  • 50
  • 191
  • 378
TheKojuEffect
  • 20,103
  • 19
  • 89
  • 125

9 Answers9

55

For angular 4.3+ and @angular/common/http

It's can be done with interceptors

@Injectable()
export class ExampleInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const url = 'http://myurl.com';
    req = req.clone({
      url: url + req.url
    });
    return next.handle(req);
  }
}

app.module.ts

import { NgModule } from '@angular/core';
import { Injectable } from '@angular/core';
import { HttpClientModule, HttpRequest, HTTP_INTERCEPTORS } from '@angular/common/http';

@NgModule({
  declarations: [
    AppComponent,
    ...
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    ...
  ],
  providers: [
    AppComponent,
    { provide: HTTP_INTERCEPTORS, useClass: ExampleInterceptor, multi: true }
  ],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

EDIT: HttpClient and HttpInterceptor are introduced in Angular 4.3

Kees de Kooter
  • 7,078
  • 5
  • 38
  • 45
vegazz
  • 1,441
  • 12
  • 15
  • 3
    NB: In order to avoid hard-coding the url in the interceptor, one may benefit from DI by doing something like this: `constructor(@Inject('BASE_URL') private baseUrl: string) {}` – kmos.w Dec 12 '17 at 20:12
18

UPDATE: See @vegazz answer for Angular 4.

For Angular 2.2.1, the following should prefix web api base url and represents a smaller footprint:

import {Request, XHRBackend, XHRConnection} from '@angular/http';

@Injectable()
export class ApiXHRBackend extends XHRBackend {
    createConnection(request: Request): XHRConnection {
        if (request.url.startsWith('/')){
            request.url = 'http://localhost:3000' + request.url;     // prefix base url
        }
        return super.createConnection(request);
    }
}

Inject into providers in app.module.ts:

providers: [
    { provide: XHRBackend, useClass: ApiXHRBackend }
  ]

Usage example:

this._http.post('/api/my-route', {foo: 'bar'})
            .map<MyResponse>(res => res.json())

It seems to cover all methods (GET, PUT, POST, ...)

Stefan Falk
  • 23,898
  • 50
  • 191
  • 378
corolla
  • 5,386
  • 1
  • 23
  • 21
  • 2
    This seems to be the cleanest way to deal with it. I made a slight change to your code and am using `localStrategy.getBaseHref()` so that I can set the `base href` in the HTML to `/dev` and the code will execute only then. In my case, I only needed this url `rewrite` for when I'm working locally. – tftd Mar 29 '17 at 15:25
  • 1
    Just wanted to add that I tested this in Angular 4.3.0 and it still works like a charm. Thanks! – anderaus Aug 12 '17 at 09:02
  • 1
    Doesn't work with angular 4 and HttpClientModule. But its simplier with interceptors. See answer below. – vegazz Sep 04 '17 at 12:18
  • 1
    It still working in angular 6 but Just for HttpModule not for HttpClientModule – Abel Valdez Oct 23 '18 at 19:11
11

For Angular2 Version 2.2 (As for Dec 2016)

Angular from RC5 mark HTTP_PROVIDERS as deprecated and trying to move things into @NgModule, the above solution is not really applicable, so as their documentation. I cross referred several other answers and found my way to implement base url, hope this might be helpful to someone else.

The basic idea is, instead of doing things in bootstrap, we move things to AppModule.

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpModule, RequestOptions } from '@angular/http';


import { CustomRequestOptions } from './customrequest.options';

@NgModule({
  declarations: [
    AppComponent,
    ...
  ],
  imports: [
    BrowserModule,
    HttpModule,
    ...
  ],
  providers: [
    { provide: RequestOptions, useClass: CustomRequestOptions }
  ],
  bootstrap: [ AppComponent ]
})

And move CustomRequestOptions into a separate injectable service

import { Injectable } from '@angular/core';
import { BaseRequestOptions, RequestOptions, RequestOptionsArgs } from '@angular/http';

@Injectable()
export class CustomRequestOptions extends BaseRequestOptions {
  merge(options?:RequestOptionsArgs):RequestOptions {
    options.url = 'http://localhost:9080' + options.url;
    return super.merge(options);
  }
}

Edit for request method other than GET.

If you are trying to send request type other than GET, the previous method fails to inject the baseurl into the request. This is because Angular2 generate new RequestOptions other than this._defaultOptions whose merge method isn't being override by our CustomRequestOptions. (See the source code here).

So instead of returning super.merge(...) in the last step of CustomRequestOptions merge method, I generated a new instance of CustomRequestOptions to ensure the following operation will still work.

import { Injectable } from '@angular/core';
import { RequestOptions, RequestOptionsArgs } from '@angular/http';

@Injectable()
export class CustomRequestOptions extends RequestOptions {
  merge(options?: RequestOptionsArgs): RequestOptions {
    if (options !== null && options.url !== null) {
      options.url = 'http://localhost:9080' + options.url;
    }
    let requestOptions = super.merge(options)
    return new CustomRequestOptions({
      method: requestOptions.method,
      url: requestOptions.url,
      search: requestOptions.search,
      headers: requestOptions.headers,
      body: requestOptions.body,
      withCredentials: requestOptions.withCredentials,
      responseType: requestOptions.responseType
    });
  }
}

And this works for POST, PUT, DELETE method as well. Hope this is helpful.

Stefan Falk
  • 23,898
  • 50
  • 191
  • 378
yeelan
  • 1,387
  • 2
  • 16
  • 22
  • im getting an error: Error: Can't resolve all parameters for CustomRequestOptions: (?). at SyntaxError.BaseError [as constructor]... any ideas? – sawe Jan 15 '17 at 13:48
  • Which version of Angular you are running on? I found this [new issue](https://github.com/angular/angular/issues/13609) on github and I wonder if it is related to the version. – yeelan Jan 15 '17 at 23:27
  • @angular/core@^2.3.1, i will see what i can get from that link that you posted. ta – sawe Jan 16 '17 at 04:51
  • @sawe Any luck with your research? I'm using angular@2.4.5 and angular-cli@1.0.0-beta.26. – sax Jan 31 '17 at 09:40
  • ended up creating my own HttpClient, that simply makes use of the angular http and adds the headers etc as well, in addition to using my prefered api url: – sawe Feb 02 '17 at 08:00
  • @sawe Care to share your implementation? The above solution works but if I make use of AOT compilation it breaks with : Property 'RequestOptionsArgs' does not exist on type 'typeof ".../node_modules/@angular/http/src/interfaces"'. – Tersius Feb 02 '17 at 08:14
  • https://plnkr.co/edit/57APx3tOovFDJLjLpbfo I have added some of the relevant files (4 in total), – sawe Feb 02 '17 at 08:46
7

Different approach: during development running your app in localhost, consider configuring a proxy instead.

proxy.conf.json

{
   "/api/**": {
      "target": "http://10.7.18.21:8080/",
      "secure": false,
      "logLevel": "debug"
   }
}

More: link

Stefan Falk
  • 23,898
  • 50
  • 191
  • 378
István Békési
  • 993
  • 4
  • 16
  • 27
  • 3
    This needs to be upvoted more. All of the solutions above are just simply overengineered just for one little prefix. – OzzyTheGiant Oct 10 '18 at 15:44
  • remember to start the server with `ng serve --proxy-config proxy.conf.json --open` – Mike D3ViD Tyson Jun 21 '19 at 00:12
  • Thats fine for ng serve but it doesn't work for production as it's not an option for ng build – Dodi Jun 18 '20 at 12:16
  • Indeed, this approach assumes that the Angular application will be deployed to the same backend target, so you do not proxy your requests in production. – István Békési Jun 19 '20 at 07:31
  • However it is perfectly fine if your angular application is available at http://10.7.18.21:8080/ while http://10.7.18.21:8080/api is be the backend for it. – István Békési Jun 19 '20 at 07:37
6

In Angular 2.0.0-beta.6 You can achieve this by overriding 'merge'

import {BaseRequestOptions, RequestOptions, RequestOptionsArgs} from 'angular2/http';

export class ApRequestOptions extends BaseRequestOptions {

  merge(options?:RequestOptionsArgs):RequestOptions {
    options.url = 'http://10.7.18.21:8080/api' + options.url;
    return super.merge(options);
  }

}
Stefan Falk
  • 23,898
  • 50
  • 191
  • 378
  • 4
    Just found out that it is working for `GET` and `DELETE` but not for `POST` and `PUT`. :/ – TheKojuEffect Feb 19 '16 at 18:59
  • As @TheKojuEffect mentioned this solution doesn't work with some methods using more elegant API. However if you choose to use more verbose one: _http.request(..)_ it seems to work. – Adrian Sobiczewski Feb 27 '16 at 18:56
  • actually, I found it not so easy to implement this, as super.merge actually returns a `RequestOptions` instance which, in case of [Http#post](https://github.com/angular/angular/blob/master/modules/angular2/src/http/http.ts#L131) a new `RequestOptions` instance is created before merging with the old one. Long story short, I really had to override merge and return a custom instance like this: https://gist.github.com/rweng/1d36dd91842f3cb58ebb – rweng Mar 10 '16 at 16:46
  • It doesn't works for **http.post** and **http.put** , but it works for **http.request** , so you can have something like : **this.http.request('/user/1', { body: body, method: 'PUT', headers: headers })** – Tiberiu Popescu Apr 18 '16 at 22:06
5

After having a look at code sources for BaseRequestOptions, RequestOptions and Http classes:

It appears that the url property corresponds to the default URL but not a prefix for URLs.

To implement your use case, I would suggest to put a service in front of the http object and inject then the service. Something like that:

import {Injectable} from '@angular/core';
import {Http} from '@angular/http';

@Injectable()
export class HttpClient {
  http: Http;
  urlPrefix: string;

  constructor(http: Http) {
    this.http = http;
    this.urlPrefix = 'http://...';
  }

  get(url) {
    return this.http.get(this.urlPrefix + url);
  }

  post(url, data) {
    return this.http.post(this.urlPrefix + url, data);
  }
}

And

import {HttpClient} from './http-client';

export classMyComponent {
  constructor(httpClient: HttpClient) {
    this.httpClient = httpClient;
  }

  handleSomething() {
    this.httpClient.post(url, data)
    ).subscribe(...);
  }
}

That said such could perhaps be contributed to Angular2 itself ;-)

Hope it helps you, Thierry

Stefan Falk
  • 23,898
  • 50
  • 191
  • 378
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
1
import {LocationStrategy} from 'angular2/router';

constructor(private locationStrategy:LocationStrategy) {
  console.log(locationStrategy.prepareExternalUrl('xxx'));
}

See also https://github.com/angular/angular/blob/1bec4f6c6135d7aaccec7492d70c36e1ceeaeefa/modules/angular2/test/router/path_location_strategy_spec.ts#L88

Stefan Falk
  • 23,898
  • 50
  • 191
  • 378
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
0

For currently user, here is really working example in angular 2.4.8

here is the code for why split and chained BaseCommonRequestOptions and CommonRequestOptions.

import { BaseRequestOptions, Headers, RequestOptions, RequestOptionsArgs } from '@angular/http';
import { Injectable } from '@angular/core';

@Injectable()
export class BaseCommonRequestOptions extends BaseRequestOptions {

  merge(options?: RequestOptionsArgs): RequestOptions {
    return new CommonRequestOptions(super.merge(extracted(options)));
  }
}

/**
 * for inner merge when using post put patch delete...others method
 */
export class CommonRequestOptions extends RequestOptions {
  merge(options?: RequestOptionsArgs): RequestOptions {
    return new RequestOptions(super.merge(extracted(options)));
  }
}

/**
 * inject default values
 *
 * @param options
 * @returns {RequestOptionsArgs}
 */
export function extracted(options: RequestOptionsArgs) {
  console.log(options);
  if (!validUrl(options.url)) {
    options.url = 'http://localhost:3000' + (options.url ? options.url : "");
  }
  // use default header application/json, if content-type header was empty.
  if (options.headers != null) {
    let contentType = options.headers.get('content-type');
    if (contentType == null || contentType == '') {
      options.headers.append('content-type', 'application/json');
    }
  } else {
    options.headers = new Headers({ 'content-type': 'application/json' });
  }

  return options;
}

/**
 * validate url
 *
 * @param url
 * @returns {boolean}
 */
export function validUrl(url: string) {
  return /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/.test(url);
}
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
0

create a .ts file

import { Injectable } from '@angular/core';
import {Request, XHRBackend, XHRConnection} from '@angular/http';


    @Injectable()
    export class ApiXHRBackend extends XHRBackend {
    createConnection(request: Request): XHRConnection {
    if (request.url.startsWith('/api')){
    var url=request.url.replace("/api", "");
    request.url = 'http://localhost:8080' + url; // prefix base url
    }
    return super.createConnection(request);
    }

}

Then in app.module.ts

import { ApiXHRBackend } from './guard/httpintercepter';
import {Request, XHRBackend, XHRConnection} from '@angular/http';

Add provider in providers section

providers: [
{provide: XHRBackend, useClass: ApiXHRBackend }
],

then in your service.ts http call must be like below example

return this._http.post("/api/customerservice/loginvalidation",data)
.map((response: Response) => {
return response;
});

Here /api will be replaced by your base URL http://localhost:8080

more details http://shabeen.in/webschool/how-to-set-angular-2-service-http-base-url/

codelover
  • 131
  • 1
  • 10