How to Build an Angular 9 App That Connects with a REST API
In this brief guide, I’ll be demonstrating how Angular 9 applications can connect to a REST API.
We will do the following:
- Create an Angular 9 project
- Set up a fake API server that you can replace with a real one
- Make
GET
requests from our project to the server and handle errors - Display all entries in the response onto the browser using Angular Material
Throughout this article, we’ll be using three terminals:
- Terminal 1 will be running our Angular project.
- Terminal 2 will be running a server running our fake API.
- Terminal 3 will be used to modify our Angular project.
1. Install Angular CLI
Let’s start by opening up Terminal 1 and installing the Angular 9 CLI. This gives us access to the ng
command, which is how we’ll be interacting with our Angular projects. We’ll use npm
to install @angular/cli
.
# Terminal 1
npm install -g @angular/cli
At the time of writing this guide, I am running version 9.1.2
for Angular CLI. You can check your version number by running ng version
and looking at Angular CLI: x.x.x
.
2. Create an Angular 9 Project
Navigate to the folder in which you’d like to create your Angular project, and create your project using ng new
.
We’ll create our project in our home directory ~
and then navigate into the Angular project.
# Terminal 1
cd ~
ng new angular-demo
Next, we’re going to start up our local development server using ng serve
, which will start our server for us and watch for file changes.
# Terminal 1
cd angular-demo
ng serve --open
We want to see this output.
** Angular Live Development Server is listening on localhost:4200,
open your browser on http://localhost:4200/ **
Compiled successfully.
This means our Angular application is available to view at http://localhost:4200
, which should open up automatically due to the --open
flag.
Making Changes
Whenever we make a change to the code in our ~/angular-demo
folder, the local server will send a web socket message to the client browser notifying a change, forcing a browser refresh.
So when we save a change, our application will automatically recompile.
If we have any errors in our code, Terminal 1 will show all these errors.
If not, we’ll see the Compiled successfully
message in the last line of the output.
Great! Our Terminal 1 is now set up to serve our Angular application.
3. Set Up a REST API
In this section, we’ll be using json-server
and Faker.js
to mock a real REST API. You can replace this with an external API service if you have one available.
Since a REST API generally functions as a separate entity, we’ll create a new folder next to angular-demo
called angular-demo-api
, which will hold our fake API.
Let’s open up Terminal 2, create ~/angular-demo-api
, and install json-server
and faker
.
We will need to run npm init
inside this folder, which will prompt us for a lot of information. Just hit Enter to use all the default options for the package.json
setup. It’s not too important what those values are.
# Terminal 2
mkdir ~/angular-demo-api
cd angular-demo-api
npm init
npm install --save json-server faker
Inside ~/angular-demo-api
, let’s create a generate.js
file that will create 100 fake entries in our so-called “database”, which will be database.json
.
// generate.js
let faker = require('faker');
let database = { products: [] };
for (let i = 1; i <= 100; i++) {
database.products.push({
id: i,
name: faker.commerce.productName(),
description: faker.lorem.sentences(),
price: faker.commerce.price(),
quantity: faker.random.number()
});
}
console.log(JSON.stringify(database));
We now want to generate the fake entries using generator.js
and store it into database.json
.
We will add generator
and server
scripts inside package.json
.
// package.json
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"generator": "node generator.js > database.json",
"server": "json-server --watch database.json"
}
Let’s generate the fake data and then run the REST API server.
# Terminal 2
npm run generator
npm run server
Our REST API server will be available from http://localhost:3000
.
These are the endpoints offered by our fake API (we will only be demonstrating GET /products
).
GET /products
for getting the productsGET /products/<id>
for getting a single product by idPOST /products
for creating a new productPUT /products/<id>
for updating a product by idPATCH /products/<id>
for partially updating a product by idDELETE /products/<id>
for deleting a product by id
Perfect. Our Terminal 2 is now set up to handle our API requests.
4. Set Up Angular Service to Handle REST API
Firstly, our service will use HttpClient
to make requests to our REST API.
Inside src/app/app.module.ts
, let’s import HttpClientModule
and add it to our imports
array.
// src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Secondly, our Angular service will handle any communication with our REST API server and appropriately set up the HttpClient
requests.
Let’s open up Terminal 3 to do this.
# Terminal 3
ng generate service data
In a text editor, let’s open the newly created src/app/data.service.ts
and call the get()
method from HttpClient
to make a GET
request to our fake API server.
// src/app/data.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class DataService {
private API_SERVER = "http://localhost:3000/products";
constructor(private http: HttpClient) { }
public sendGetRequest() {
return this.http.get(this.API_SERVER);
}
}
We’ll test out this functionality in our app
component for simplicity.
When creating an API tool, be sure to create new components with ng g c component_name
to make use of Angular’s modularity.
Inside app.component.ts
, we’ll call sendGetRequest()
upon initialization of this app
component.
// src/app/app.component.ts
import { Component, OnInit } from '@angular/core';
import { DataService } from '../data.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'angular-demo';
products = [];
constructor(private dataService: DataService) { }
ngOnInit() {
this.dataService.sendGetRequest().subscribe((data: any[])=>{
console.log(data);
this.products = data;
});
}
}
We can actually ensure that our Angular application is successfully communicating to our API server by heading over to http://localhost:4200
in our browser.
Let’s open up our Developer Console by pressing Ctrl
+Shift
+I
on Windows or Ctrl
+Option
+I
on Mac.
Since we logged the response into our console, we should see an array of 100 objects printed to our console.
(100) [{...}, {...}, {...}, {...}, ...]
Now all we have to do is display this onto our browser.
5. Set Up a UI
We’re going to use Angular Material to set up our user interface. Let’s add that right now.
# Terminal 3
ng add @angular/material
We’ll have to import the modules in app.module.ts
just as we did with HttpClient
.
// src/app/app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-brower/animations';
import { AppComponent } from './app.component';
import { HttpClientModule } from '@angular/common/http';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatIconModule } from '@angular/material/icon';
import { MatCardModule } from '@angular/material/card';
import { MatButtonModule } from '@angular/material/button';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
HttpClientModule,
BrowserAnimationsModule,
MatToolbarModule,
MatIconModule,
MatButtonModule,
MatCardModule,
MatProgressSpinnerModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
We will now populate our app.component.html
using these modules. This file will contain all the default HTML for all Angular projects, so we’ll first clear the entire file and replace it with this.
In this section, products
refers to products = [];
inside app.component.ts
. We are iterating through products
using *ngFor="let p of products"
, which will create a new instance of mat-card
per product p
.
This won’t be the prettiest application you’ll ever see, but it’ll be a functional starting point.
Let’s open up src/app/app.component.html
.
<div style="padding: 5px;">
<mat-spinner *ngIf="products.length === 0"></mat-spinner>
<mat-card *ngFor="let p of products" style="margin-top:5px;">
<mat-card-header>
<mat-card-title>{{p.name}}</mat-card-title>
</mat-card-header>
<mat-card-content>
<p>{{p.description}}</p>
</mat-card-content>
<mat-card-actions>
<button mat-button>View Product</button>
</mat-card-actions>
</mat-card>
</div>
There you have it! Once your application recompiles, you should be able to view all the data from our REST API on your browser.
6. Improving Our Angular Service
In this section, we’ll quickly improve data.service.ts
to include client-side and server-side error handling.
JavaScript errors will return ErrorEvent
objects while server and database errors will return HTTP Error Responses
.
We can create a method errAlert()
to respond to errors, catch any errors from our GET
request using catchError(this.errAlert)
, and retry sending failed HTTP requests three times using retry(3)
.
// src/app/data.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { throwError } from 'rxjs';
import { retry, catchError } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class DataService {
private API_SERVER = "http://localhost:3000/products";
constructor(private http: HttpClient) { }
errAlert(error: HttpErrorResponse) {
let msg = 'Error unknown';
if (error.error instanceof ErrorEvent)
msg = `Error: ${error.error.message}`;
else
msg = `Error: ${error.message}`;
window.alert(msg);
return throwError(msg);
}
public sendGetRequest(){
return this.http.get(this.API_SERVER).pipe(retry(3),catchError(this.errAlert));
}
}
Conclusion
This was a a quick demonstration of how we can communicate with our REST API server from an Angular 9 application.
While this serves as a great template, there are many more standards that come with developing Angular applications.
Be sure to make use of your components instead of stuffing all your code into the root component, handle HttpClient
Observables properly using takeUntil()
, support sending query or URL parameters in our requests to the API server using HttpParams
, and handle all basic CRUD operations (GET, POST, PUT, DELETE) inside our Angular service.
I hope this tutorial provided some basic building blocks for your Angular applications.