Beginner developers often don't know how to simulate real-world scenarios for practice and don't know where to get data from.
In this chapter we will therefore see how to set up a fake REST API server and consume data from an Angular application.
You will learn how to fetch data from REST API (GET
) and remove elements from the database (DELETE
) by using the HTTPClient
Angular service.
json-server
json-server
HttpModule
DELETE
and POST
methodssubscribe
?json-server
json-server
is an opensource tool that allow you to create full (fake) REST API with zero coding in few seconds.
I think it's really a cool way to create REST API:
You can simply create a JSON
file in which :
db.json
{
"posts": [
{ "id": 1, "title": "Item 1" },
{ "id": 2, "title": "Item 2" },
],
"comments": [
{ "id": 1, "body": "a comment", "postId": 1 }
],
"profile": { "name": "Fabio Biondi" }
}
The previous JSON
file creates 3 endpoints:
http://localhost:3000/posts
http://localhost:3000/comments
http://localhost:3000/profile
Now you can query/mutate your JSON database using GET
to retrieve data, POST
to write, PATCH
/PUT
to update and DELETE
to remove an item.
But you can do much more:
http://localhost:3000/posts/[ID]
http://localhost:3000/posts?q=[TEXT]
http://localhost:3000/posts?_sort=views&_order=asc
/posts?_embed=comments
json-server
First, open a new terminal in the project root folder and install json-server
as devDependency (using the -D
parameter):
DevDependencies are the packages a developer needs during development and not in production just like this fake server, compilers, tools for doc generations, unit and E2E tests and so on...
npm install json-server -D
Usually your API can be located and developed in a completely new project / repository but, while you're learning, I suggest you to install JSON-SERVER as devDependency
of your front-end project.
In this way, next time you'll clone or share the project you will have everything already in place and you're ready to go in few minutes without any further configuration, or clone another repository.
Now create a JSON file to generare your database in ./[YOUR ROOT FOLDER]/server/db.json
/server/db.json
{
"users": [
{
"id": 1,
"label": "Fabio",
"gender": "F",
"age": 20
},
{
"gender": "F",
"label": "Marianna",
"id": 2,
"age": 21
},
{
"gender": "M",
"label": "Mario",
"id": 3,
"age": 20
}
]
}
Your project folder should look like the following:
After installing JSON-Server, open package.json
, locate the scripts
node and add following command:
package.json
"scripts": {
# ... others scripts here ...
"server": "json-server --watch server/db.json"
}
Open a new terminal window, go to the project root folder and run following script:
npm run server
IMPORTANT: don't kill the terminal in which you're running Angular CLI.
Both terminals must run at the same time
Now you can consume data from your endpoints. For instance:
• http://localhost:3000/users (GET)
• http://localhost:3000/users/1 (DELETE)
• http://localhost:3000/users/1 (POST, PUT, PATCH) and pass the user as payload
The endpoint now supports CORS and all REST methods: GET, POST, DELETE, PUT, PATCH
HttpModule
Angular provides a useful service to communicate with your server Api: HttpClient
.
Anyway, in order to use it in your projects, first you need to import HttpClientModule
in AppModule
:
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http'; // NEW
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpClientModule // NEW
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Now you can inject and use HttpClient
service to load data from server.
Dependency Injecton is a mechanism that allow you to inject a class (aka Service) rather than create an instance.
Read more in the Angular Documentation and go to the next chapter to create your own service.
In fact, now you can replace all previous AppComponent
class content with the following in order to:
HttpClient
service in the class constructor/users
endpoint when the application starts and populate the this.users
array.HTML template does not need to be updated so you only need to change the component class.
app.component.ts
export class AppComponent {
// init to empty array
users: User[] = [];
/* base API url */
URL = 'http://localhost:3000';
// Inject HttpClient Service
constructor(private http: HttpClient) {
this.init();
}
init() {
// Get all users from REST API
this.http.get<User[]>(this.URL + '/users')
.subscribe(res => {
// populate the `users` array
this.users = res;
});
}
deleteHandler(userToRemove: User) {
// MISSING: we should invoke the endpoint to delete the item from the database
this.users = this.users.filter(u => u.id !== userToRemove.id);
}
saveHandler(f: NgForm) {
// MISSING: we should invoke the endpoint to add a new item to the database
const user = f.value as User;
user.id = Date.now(); // add a fake ID
this.users = [...this.users, user]
f.reset({ gender: '' });
}
}
The deleteHandler
and saveHandler
methods still don't update the database yet since we still have to invoke the endpoints.
You will receive an compiler error if you don't import HttpClient
in AppComponent
:
So, don't forget to import it:
app.component.ts
import { HttpClient } from '@angular/common/http';
Usually we should invoke and consume an API in a lifecycle hook of the component called ngOnInit
but now I prefer invoke the API in the costructor to keep the article as simple as possible.
You should be able now to load and display all available users of your JSON database:
DELETE
and POST
methodsNow we update deleteHandler
and saveHandler
methods to invoke the REST API in order to update our database when adding or removing users.
Furthermore we also want to clean the form when a new user is added.
So update your class methods as shown below:
app.component.ts
deleteHandler(userToRemove: User) {
// Remove User from db.json
this.http.delete(`${this.URL}/users/${userToRemove.id}`)
.subscribe(() => {
// Update `users` array removing the element from the list
this.users = this.users.filter(u => u.id !== userToRemove.id);
});
}
saveHandler(f: NgForm) {
// get form data (it does not contain the `id`)
const user = f.value as User;
// add the user to db.json
this.http.post<User>(`${this.URL}/users/`, user)
.subscribe((dbUser) => {
// Add the returned user (with `id`) to the `users` array
this.users = [...this.users, dbUser]
// reset form to "pristine" status
f.reset({ gender: '' });
});
}
Now you can remove and add users from the database. In fact, your JSON file should be updated when users are added or removed.
app.component.ts
import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core';
import { NgForm } from '@angular/forms';
import { User } from './model/user';
@Component({
selector: 'app-root',
template: `
<div class="container">
<h1>Users</h1>
<form
class="card card-body mt-3"
#f="ngForm"
(submit)="saveHandler(f)"
[ngClass]="{
'male': f.value.gender === 'M',
'female': f.value.gender === 'F'
}"
>
<input
type="text"
[ngModel]
name="label"
placeholder="Add user name"
class="form-control"
required
#labelInput="ngModel"
[ngClass]="{'is-invalid': labelInput.invalid && f.dirty}"
>
<select
[ngModel]
name="gender"
class="form-control"
required
#genderInput="ngModel"
[ngClass]="{'is-invalid': genderInput.invalid && f.dirty}"
>
<option value="">Select option</option>
<option value="M">M</option>
<option value="F">F</option>
</select>
<button
class="btn"
[disabled]="f.invalid"
[ngClass]="{
'btn-success': f.valid,
'btn-danger': f.invalid
}"
>Save</button>
</form>
<hr>
<!--LIST BELOW: NO CHANGES HERE-->
<hr>
<ul class="list-group">
<li
*ngFor="let u of users" class="list-group-item"
[ngClass]="{
'male': u.gender === 'M',
'female': u.gender === 'F'
}"
>
<i
class="fa fa-3x"
[ngClass]="{
'fa-mars': u.gender === 'M',
'fa-venus': u.gender === 'F'
}"
></i>
{{u.label}}
<i class="fa fa-trash fa-2x pull-right" (click)="deleteHandler(u)"></i>
</li>
</ul>
</div>
`,
styles: [`
.male { background-color: #36caff; }
.female { background-color: pink; }
.card { transition: all 0.5s }
`]
})
export class AppComponent {
users: User[] = []; // init to empty array
URL = 'http://localhost:3000'; /* server url */
constructor(private http: HttpClient) {
this.init();
}
init() {
this.http.get<User[]>(this.URL + '/users')
.subscribe(res => {
this.users = res;
});
}
deleteHandler(userToRemove: User) {
this.http.delete(`${this.URL}/users/${userToRemove.id}`)
.subscribe(() => {
this.users = this.users.filter(u => u.id !== userToRemove.id);
});
}
saveHandler(f: NgForm) {
const user = f.value as User;
this.http.post<User>(`${this.URL}/users/`, user)
.subscribe((dbUser) => {
this.users = [...this.users, dbUser]
f.reset({ gender: '' });
});
}
}
The result is similar to the previous one but data are now saved on database (db.json
):
The following interactive code playground does not work as expected because we need to start a local JSON-SERVER.
Run a local JSON server to see some products!
subscribe
?As you may have notice, we need to subscribe the HttpClient
to invoke a REST API .
Why and what is it?
HttpClient
returns an Observable
and emit some data when the request is fullfilled (and an error when it fails).
But we must subscribe it if we want to get the emitted value.
You can imagine an Observarble
just like a stream of data that you have to subscribe to receive data from it.
It might seems a Promise
at a first glance but it's much more.
This topic is very complex and hard to explain in few lines but now you only need to know that the HttpClient
service returns a (cold) Observable, that is a "producer" that is created and activated only when subscribed.
Often, when we're talking about Observable we're referring to RxJS, an amazing library that is closely coupled in Angular: more or less everything in Angular is handled by Observables.
When you start using Angular you can pretend it doesn't exist but over time you will need to improve your RxJS skills, otherwise you won't be able to take full advantage of the framework.
RxJS and Reactive Programming is a complex topic but don't worry. You have time to learn more about it.
Anyway, the HttpClient
returns a "special" type of Observable that just emit one value, the result of the HTTP request, or an error, in case it doesn't work for any reason.
A common question is: why Promise is not enough to handle a simple HTTP request?
Because thanks to RxJS we can create sequences of observable mixing reactive data from forms, router, components, multiple data sources and do other "magical" stuff.
I know, it's very hard to imagine when you're a beginner but I suggest to have fun with Angular and think about RxJS later.
Anyway you can also find several videos about RxJS in my Youtube Channel