ngconf
Published in

ngconf

How to proxy HTTP requests in Angular

Hand-drawn picture of 2 girls talking via phone and one girl as a telephonist between them

In this story you will learn how to proxy HTTP requests in Angular. We will not only look at the code and the implementation details, but also briefly talk about the use case itself. Why it could be useful to proxy your requests?

Let’s assume that you want to build a dashboard to display some bitcoin related information (anyone seems to be interested in this topic these days). You already have some data which is available at http://api.bitcoincharts.com/v1/markets.json. You also plan to deploy your frontend app under this domain.

So far, so good. You can request the desired data using a HttpClient from Angular in your service (do not forget to import HttpClientModule in app.module.ts).

//bitcoin-market-data.service.tspublic fetchBitcoinData() {
return this.http.get<BitcoinMarketData[]>.
(‘http://api.bitcoincharts.com/v1/markets.json’);
}

You could then consume it in your component. Below is the simplified code.

//app.component.tsexport class AppComponent {
public bitcoinData=this.bitcoinService.fetchBitcoinData();
public constructor(private bitcoinService:
BitcoinMarketDataService) {}
}
//app.component.html<div *ngFor=”let bitcoinDataElement of bitcoinData | async”>
<p>{{ bitcoinDataElement.bid }}</p>
<p>{{ bitcoinDataElement.ask }}</p>
</div>

At the first glance, the code looks ok. However, what if you have different environments and want to deploy your app there without touching the source code? For example, you have https://bitcoincharts.dev.com and https://bitcoincharts.staging.com to test your app before shipping to production? The easiest way to do so would be to use a relative path, i.e. /v1/markets.json for this.

So, let’s change the path and start the app. As you might’ve guessed, it would be too easy if it already worked. Unfortunately no data is coming in…

core.mjs:6449 ERROR HttpErrorResponse {headers: HttpHeaders, status: 0, statusText: ‘Unknown Error’, url: ‘https://bitcoincharts.com/v1/markets.json', ok: false, …}

Though the usual CORS error looks slightly different, the fact is — your backend does not like receiving requests from unmatching origins (which localhost is). So, our first use case is: proxying a request from localhost to the remote backend service.

Let’s have a look at the solution. Luckily, it is well documented in the “Angular Guide: Building & Serving”, devServer.proxy and http-proxy-middleware, because Angular makes use of webpack’s dev server which utilizes the http-proxy-middleware. So, to configure our proxy we just have to create a config file, e.g. proxy.conf.js (naming irrelevant).

//proxy.conf.jsvar defaultTarget = ‘http://api.bitcoincharts.com/';
module.exports = [
{
context: [‘/v1/**’],
target: defaultTarget,
changeOrigin: true,
}
];

As you can see, we’ve defined some parameters for our proxy. You will find far more possible configuration options in the documentation that I linked above.

  • targetpoints to the destination of our proxy.
  • The origin of the host header is kept when proxying by default, you can set changeOrigin to true to override this behaviour. This is particularly useful if you need to access a backend that is not on localhost.
  • context is not really necessary for our tiny example. However, in a real-world application you usually want to proxy multiple HTTP request to various APIs. To do so, you can add additional APIs to the context array.

You can store this file anywhere in your project, but it’s usually placed on the top level or in the /src folder. All you need to do now is to point to it in the “serve” section of angular.json.

//angular.json“serve”: {
“builder”: “@angular-devkit/build-angular:dev-server”,
“options”: {
“browserTarget”: “proxy-for-angular:build”,
“proxyConfig”: “proxy.conf.js”,
“port”: 4200
},
“configurations”: {
“production”: {
“browserTarget”: “proxy-for-angular:build:production”
},
...
},
...
}
...

This configuration basically tells Angular: “Whatever you run this app on a development server — in production or development mode — take the proxy which is configured in proxy.conf.js and use port 4200 for this”. In case you want to see the whole project, check out the project on GitHub.

Speaking of ports — what if your backend is also running on localhost? Proxy has you covered, too. Hence, the second use case is proxying a localhost request to the localhost backend service listening to a different port.

To do so, you just need to replace the defaultTarget in your proxy.conf.js. In case your backend runs on http://localhost:3000, change the config as follows.

//proxy.conf.jsvar defaultTarget = ‘http://localhost:3000';
module.exports = [
{
context: [‘/v1/**’],
target: defaultTarget,
secure: false,
}
];

You no longer need to define changeOrigin astrue because your frontend app no longer has a different origin. However, you might want to setsecure to false to accept responses from a backend server running on HTTPS with an invalid certificate.

If you don’t have the source code of your backend but still need to manipulate the responses, a fake backend comes in handy. This is our third use case: proxy to the remote backend service or to the localhost fake backend service depending on the parameters. Here is an example with json-server.

//proxy.conf.jsvar defaultTarget = ‘http://api.bitcoincharts.com/';
module.exports = [
{
context: [‘/v1/**’],
target: process.env['FAKE_BACKEND']?
'http://localhost:3000':defaultTarget,
secure: false,
}
];

To use the environment variable we have to modify our script in package.json as follows.

“scripts”: {
...
“start”: “ng serve”,
“start:fakebackend”: “FAKE_BACKEND=true ng serve”,
...
}

Now we have the option of starting our app with a fake backend by setting FAKE_BACKEND variable to true. Our proxy adjusts its target accordingly.

Finally, I have a rather rare use case for a proxy — what if you want to use different proxy configs for different apps in your monorepo? Let’s say, your microfrontends come from different style guides or you need to proxy to various authorisation providers depending on the app. In this case you just define multiple config files and link them in angular.json accordingly.

In the following example we have a red and a blue app which receive their styling from www.red-app.com/api and www.blue-app.com/api respectively. Your config files would then look like this.

//red-proxy.conf.jsvar defaultTarget = ‘http://red-app.com/';
module.exports = [
{
context: [‘/api’],
target: defaultTarget,
}
];
//blue-proxy.conf.jsvar defaultTarget = ‘http://blue-app.com/';
module.exports = [
{
context: [‘/api’],
target: defaultTarget,
}
];

angular.json has then the following entries:

//angular.json"red-app": {
...
“serve”: {
“builder”: “@angular-devkit/build-angular:dev-server”,
“options”: {
“browserTarget”: “proxy-for-angular:build”,
“proxyConfig”: “red-proxy.conf.js”,
“port”: 4200
},
...
},
"blue-app": {
...
“serve”: {
“builder”: “@angular-devkit/build-angular:dev-server”,
“options”: {
“browserTarget”: “proxy-for-angular:build”,
“proxyConfig”: “blue-proxy.conf.js”,
“port”: 4200
},
...
},

To sum up, let me reiterate over the situations when a proxy might be useful:

  • You want to send a request from localhost frontend to a remote backend.
  • You want to send a request from localhost frontend to localhost backend.
  • You want to use remote backend and to mock data locally from time to time.
  • Some apps in your monorepo need different base domains with the same API definition.

Hm… is there any situation, when a proxy isn’t useful?

[Disclaimer: did I miss something / is something not quite correct? Please let me and other readers know AND provide missing/relevant/correct information in your comments — help other readers (and the author) to get it straight!]

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store