Feeding data from a REST endpoint to Angular components
Getting, and using, data from a REST endpoint is actually much simpler than one would assume. You just need to start getting comfortable with RxJS data streams.
A word of advice
This is the first inherently difficult topic so far in “Angular for dads”. Normally you would stop right now and go read an introduction to RxJS, Observables, and whatnot.
But you don’t have time for that.
So instead I’ll do my best to keep it light and to the point, and if it really doesn’t work out for you in the end, let me know in the comments and I’ll find some good additional resources to point you at.
From static data to an observable stream
Right now we are getting data from a service and feeding it to a component.
But what happens if the data changes over time? To get it again we could rerun the application by refreshing the page. Suboptimal.
Let’s transform the static data into an observable that changes over time:
- Install Chance.js, a faker:
npm i -S chance && npm i -D @types/chance
- Open
src/persons/persons.service.ts
and update it to emit a stream of 3 different random person objects over time:
import { Injectable } from '@angular/core';// rxjs
import { Observable, interval } from 'rxjs';
import { map } from 'rxjs/operators';// Third Parties
import { Chance } from 'chance';// App Models
import { Person } from './person.model';@Injectable()
export class PersonsService {
private chance = new Chance(); // In the future, this will fetch data from a REST endpoint
getPersons(): Observable<Person[]> {
// Emit a value every 2 seconds
return interval(2000).pipe(
map(() => {
const persons: Person[] = [
new Person({ name: this.chance.name(), age: this.chance.age() }),
new Person({ name: this.chance.name(), age: this.chance.age() }),
new Person({ name: this.chance.name(), age: this.chance.age() }),
]; return persons;
})
);
}
}
- Open
src/app/app.component.ts
and update it to use the new data stream instead of static values:
import { Component } from '@angular/core';// rxjs
import { Observable } from 'rxjs';// App Models
import { Person } from './persons/person.model';// App Services
import { PersonsService } from './persons/persons.service';@Component({
selector: 'ng4d-root',
template: `
<ng4d-greeting-card
*ngFor="let person of (persons$ | async)"
[person]="person"
></ng4d-greeting-card>
`,
})
export class AppComponent {
// Always append a `$` - for stream - to an Observable property, for clarity
persons$: Observable<Person[]>; constructor(private personsService: PersonsService) {
this.persons$ = this.personsService.getPersons();
}
}
And lo and behold: a self updating data stream!
Getting data from a REST endpoint
Ok, bear with me a little more: we are going to use Angular’s own HttpClient service to get data from a REST endpoint instead of using Chance to invent it at runtime.
Setting up a quick and dirty API server
Usually I’d fire up a backend with Sails.js, but we have a very simple use case to cover and JSON Server will do just fine.
- Install JSON Server globally:
npm install -g json-server
- Create a
rest-simulator
directory under the root of your project - Create
rest-simulator/index.js
:
const Chance = require('chance');
const chance = new Chance();module.exports = () => {
const data = { persons: [] }; // Create 3 persons
for (let i = 0; i < 3; i++) {
data.persons.push({ name: chance.name(), age: chance.age() });
} return data;
};
- Open a new terminal, move into
rest-simulator
, and launch the server:json-server index.js
- Point your browser (or REST client like Postman or
curl
) to http://localhost:3000/persons and you should see something like this JSON:
[
{
"name": "Isaac Norton",
"age": 29
},
{
"name": "Ernest Simon",
"age": 27
},
{
"name": "Eula Clarke",
"age": 23
}
]
If you perform the same request again, you’ll get the exact same results until you stop and re-launch json-server index.js
.
This is how json-server
works, and it will actually come in handy once we get to user input and forms in Angular in one of the coming stories.
Using HttpClient to get the data
Now for the fun part.
- Import
HttpClientModule
intoapp.module.ts
so that you can then useHttpClient
in every part of the application:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';// App Components
import { AppComponent } from './app.component';
import { GreetingCardComponent } from './greeting-card/greeting-card.component';// App Modules
import { PersonsModule } from './persons/persons.module';@NgModule({
declarations: [AppComponent, GreetingCardComponent],
imports: [BrowserModule, PersonsModule, HttpClientModule],
bootstrap: [AppComponent],
})
export class AppModule {}
- Update
src/persons/persons.service.ts
to start fetching data from the JSON Server backend:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';// rxjs
import { Observable } from 'rxjs';// App Models
import { Person } from './person.model';@Injectable()
export class PersonsService {
constructor(private httpClient: HttpClient) {} getPersons(): Observable<Person[]> {
return this.httpClient.get<Person[]>('http://localhost:3000/persons');
}}
Much simpler! Notice the service injection of HttpClient
in the constructor, and the .get<Person[]>()
bit which is how you tell Angular’s httpClient
to which type you expect it to map the JSON response to.
And you should be able to see the request in the dev tools.
A quick refactor
Let’s go for the extra mile and make some preparations for when we will build a production bundle of the application.
- Create a new directory:
src/config
- Create
src/config/api.config.ts:
export const apiBaseUrl = 'http://localhost:3000';// Persons
export const apiEndpointPersons = [apiBaseUrl, 'persons'].join('/');
- Update
src/persons/persons.service.ts
:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';// rxjs
import { Observable } from 'rxjs';// App Configurations
import { apiEndpointPersons } from '../config/api.config';// App Models
import { Person } from './person.model';@Injectable()
export class PersonsService {
constructor(private httpClient: HttpClient) {}
getPersons(): Observable<Person[]> {
return this.httpClient.get<Person[]>(apiEndpointPersons);
}
}
Wrapping up
All done! This time you learned how to:
- Use the
async
pipe and Observables to display an async stream of data instead of simple static values - Quickly fire up a REST backend simulator
- Use Angular’s built-in
HttpClient
to fetch data from a REST backend
If there’s something about this topic that I can help you understand better, let me know in the comments!