Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you return data to your Angular app using proxy.conf.json?

I am using proxy.conf.json as I develop my Angular application.

However I would like to, for a few endpoints, simply return a JSON object when called. Currently my proxy.conf file redirects to a locally running backend which returns these JSONs. However I'd rather not run the backend server and simply return the JSON from proxy.conf.json.

Is this possible somehow?

like image 369
CodyBugstein Avatar asked Dec 01 '22 14:12

CodyBugstein


2 Answers

It is possible by using proxy.conf.js instead of proxy.conf.json. Then you can specify a bypass function where you can return a response directly. This is mentioned in the angular-cli documentation for the proxy but it does not give many details. Here is a sample proxy.conf.js file to do it.

const PROXY_CONFIG = {
  '/api': {
    'target': 'http://localhost:5000',
    'bypass': function (req, res, proxyOptions) {
      switch (req.url) {
        case '/api/json1':
          const objectToReturn1 = {
            value1: 1,
            value2: 'value2',
            value3: 'value3'
          };
          res.end(JSON.stringify(objectToReturn1));
          return true;
        case '/api/json2':
          const objectToReturn2 = {
            value1: 2,
            value2: 'value3',
            value3: 'value4'
          };
          res.end(JSON.stringify(objectToReturn2));
          return true;
      }
    }
  }
}

module.exports = PROXY_CONFIG;

You need to recheck the url in the bypass function because it is called for all /api requests and then you just directly return a response for the ones you want the others will still be redirected to the target address. You return true to tell the proxy the request finished.

Make sure to then specify the correct file when running ng serve --proxy-config proxy.conf.js.

like image 167
AlesD Avatar answered May 20 '23 09:05

AlesD


For returning JSON from proxy.config.conf, there doesn't seem to be an easy way to do it.

One way would be to have a baseApiUrl in the environment.ts file as well urlSuffix set to .json. Then all of the API calls would have to be something like this: enviroment.baseApiUrl + uri + environment.urlSuffix. Then in environment.prod.ts, the urlSuffix would be an empty string. This is a hacky solution but would work.

Alternative using HTTP_INTERCEPTORS

A cleaner solution that leverages the framework, would be to use an HttpInterceptor with the HttpClient along with setting the baseApiUrl in the environment.ts file. This allows for different API endpoints per environment.

environment.ts

export const environment = {
   apiBaseUrl: 'http://localhost:4200/assets/api',
   production: false
}

development.interceptor.ts

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

@Injectable()
export class DevelopmentInterceptor implements HttpInterceptor {
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  let clonedRequest = null;

    if (!environment.production) {
      clonedRequest = request.clone({
        url: `${request.url}.json`
      });

      return next.handle(clonedRequest);
    }

    return next.handle(clonedRequest);
  }
}

This class will intercept any http request made by the HttpClient. Using the properties in the environment.ts file, you can check if the current build is a production build. If it is, clone the request and append .json to it. Anything that is in the assets folder is accessible from the browser. Below is a sample file that maps to the url http:localhost:4200/assets/api/test.json.

/src/assets/api/test.json

{
  "name": "test",
  "description": "Test Data"
}

Place this file in the assets directory and have the directory structure follow the endpoints of the actual API.

test.service.ts

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

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

@Injectable()
export class TestService {
  private baseUrl = `${environment.apiBaseUrl}/test`;

  constructor(private http: HttpClient) {
  }

  getTestData(): Observable<any> {
    return this.http.get(this.baseUrl);
  }
}

Import the environment.ts file here and set the base url to the apiBaseurl property. As long as you import the environment.ts file and not the environment.prod.ts file, this will work in all environments as the appropriate environment file will be read from when the app is built. So this url only has to be change in one place per environment.

Angular CLI Build Targets & Environment Files

app.component.ts

import {Component, OnInit} from '@angular/core';
import {TestService} from './test.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'app';

  constructor(private testService: TestService) {
  }

  ngOnInit() {
    this.testService.getTestData().subscribe(
      (testData) => console.log(testData)
    );
  }
}

Here the TestService is injected into the AppComponent and the getTestData() method is called to fetch data from the API. The DevelopmentInterceptor checks the environment and appends .json to the request. Then the data is logged to the console.

app.module.ts

import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';

import {AppComponent} from './app.component';
import {DevelopmentInterceptor} from './development.interceptor';
import {TestService} from './test.service';

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

Register the TestService and the DevelopmentInterceptor as providers.

Using this setup, proxy.config.json is not necessary.

For more information on HttpInterceptors, there is the Angular Documentation Intercepting Http Requests & Responses.

There is also a tutorial by Jason Watmore that does some more advanced things with this approach. Angular 5 - Mock Backend Example for Backendless Development

like image 21
josavish Avatar answered May 20 '23 09:05

josavish