In order to contact a back end server, we need a few things This will start with the proxy, but also include the client library and usage of Observables.

Setting up HTTP the proxy

The HTTP proxy will route back-end data to the front-end’s ng webserver. To accomplish this, we must modify the angular.json file to include a proxy config.

angular.json (snippet)
...
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "courseware:build",
            "proxyConfig": "proxy.config.json"
          },
...

Now, in the proxy.config.json file:

proxy.config.json
{
  "/v1/*": {
    "target": "http://127.0.0.1:6061",
    "secure": false,
    "logLevel": "debug",
    "changeOrigin": true
  }
}

Each key in this object is a path that we are forwarding to another webserver. You must ensure that the "target" matches the URL that your webserver is serving on. Mine happens to be port 6061, but yours may be different depending on what you have done. This server does not need to be a node server, but anything that responds. For example, you could create a path for static assets that is a simple webserver and assign it a path here.

Ensure that your passthrough works by launching both your back-end webserver and the Angular webserver. My back-end is in a server directory under the Angular project, so:

$ node server/server.js # In one terminal, start the backend (wherever and whatever it's called)
$ npm start # In another terminal, start the Angular frontend server

Now, we need to test that the results are as we expect. For my server, which responds to /v1/classes:

$ curl 127.0.0.1:6061/v1/classes
["P422","C311"]
$ curl 127.0.0.1:4200/v1/classes
["P422","C311"]

These two commands should have the same output if things are working correctly!

HTTPClient

Now, we must contact the back-end from inside our Angular application. You will need to add to the app.module.ts file in order to enable the HTTP client’s dependency injection throughout your application:

src/app/app.module.ts (snippet)
import {HttpClientModule} from '@angular/common/http';

// ...


  imports: [
    BrowserModule,
    AppRoutingModule,
    NgbModule,
    HttpClientModule,
  ],

// ...

Now, in the service set up to provide specific data:

src/app/syllabus-data.service.ts
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';

export class Course {
  section: string;
  title: string;
  description: string;
  gradingScale: string[];
  calendar: string[];
  bookInfo: string;
  meetingTime: string;
}

@Injectable({
  providedIn: 'root'
})
export class SyllabusDataService {
  constructor(
    private http: HttpClient
  ) {
  }

  private url = '/v1/classes';

  public getCouse(name: string): Observable<Course> {
    return this.http.get<Course>(`${this.url}/${name}`);
  }

  public getCourseNames(): Observable<string[]> {
    return this.http.get<string[]>(this.url);
  }
}

Note a few things here:

  • This service only deals with syllabus data. If I needed other kinds of data, I would create a separate service for that communication. Do not mix concerns of data services!

  • There is no static data in this file anymore.

  • We are using dependency injection to get the HttpClient. You can see this in the constructor. Angular will provide an instance of HttpClient at runtime because our constructor asks for it.

  • We are returning whatever this.http.get returns. What is that?

    • It is an Observable, which is Angular’s way of handling promises or callbacks.

  • There are other verbs than get. The http variable also contains functions for put, post, and delete as well.

Observables with rxjs

Observables are just a variation on async programming. We used promises in NodeJS backends, here we use Obserables.

You can read more about rxjs at the angular documentation: The RxJS Library.

As an executive overview, instead of having a .catch and .then method, Observables have a .subscribe function that takes a function or object. For example:

const data = http.get<Thing>(url);
data.subscribe({
  next(response) { console.log(response); },
  error(err) { console.error('Error: ' + err); },
  complete() { console.log('Completed'); }
});

Or without handling errors or completion:

apiData.subscribe(res => console.log(res.status, res.response));

This is all well and good, but it means that all objects are Observable until resolved. But your templates can resolve observables for you!

src/app/syllabus/syllabus.component.html (snippet)
  <h1>{{(selectedCourse | async)?.section}}: {{(selectedCourse | async)?.title}}</h1>

Notice that we pipe through async? Well, that causes the template to wait for the Observable to respond with a value. Easy!

Route Data with Observables

Finally, route data is also an observable. So in an Angular route like /syllabus/:class, we need to extract class and name. We do this in the ngInit method in the example, but it can be done at other times too:

this.selectedCourse = this.route.paramMap.pipe(
  switchMap((params: ParamMap): Observable<Course> => {
	return this.syllabusDataService.getCouse(params.get('class'));
  })
);

It should be noted that params has a few other tricks up its sleeve. One useful trick is the .has method:

this.selectedCourse = this.route.paramMap.pipe(
  switchMap((params: ParamMap): Observable<Course> => {
	if (!params.has('class'))
		return this.syllabusDataService.getCouse('default course');
	return this.syllabusDataService.getCouse(params.get('class'));
  })
);

Another thing that you may need to do is return static data from the paramMap. Since we are stuck in an Observable, you would need to create one:

this.selectedCourse = this.route.paramMap.pipe(
  switchMap((params: ParamMap): Observable<Course> => {
	return new Observable((subscriber) => {
		if (!params.has('class'))
			return subscriber.next(defaultValue);
		return subscriber.next(valueRelatedToClass);
	})
  })
);

Source Code

You may download the source to the video at: courseware-httpbackend.zip