Ionic 5|4 HttpClient CRUD Service Tutorial to Consume RESTful Server API

Angular’s HttpClient service enables the communication between application and server by making an HTTP call to RESTfull API endpoints. In response, we get JSON data which is consumed to show meaningful content to users in client-side applications.

In this tutorial we are going to learn, how to use HttpClient service in Ionic 5 Application to make server HTTP calls with custom headers and propper Error Handling using RxJS operators. In our student application, we will implement CRUD (Create, Read, Update and Delete) operations to communicate with a Mock Server which we will build using JSON-Server NPM package.

Let’s get started with our step-wise tutorial!

Here we will create a new Ionic 5 Application a blank template using Ionic CLI tool with the latest version v6.5.0

Note: To upgrade to Ionic’s latest version 5, we need to uninstall the ionicpackage as it is deprecated and install the @ionic/cli package

How to Upgrade Ionic 4 to latest version 5?

First, we need to uninstall the ionic package and install @ionic/cli:

$ npm uninstall ionic
$ npm install @ionic/cli

Check more on migration here.

 

Create an Ionic Application

Run the following command in terminal to create a new Ionic application of type angular with a blank template:

$ ionic start Ionic4HttpClient blank --type=angular

Add New Pages in Application

For our Students application, we will create 4 new pages and delete Home page, which is created by default with blank template.

Run the following commands to generate new pages:

$ ionic generate page student-create
$ ionic generate page student-edit
$ ionic generate page student-list
$ ionic generate page student-detail

Update Routing Module

Also, update app-routing.module.ts file to add the route paths for above new added components and remove home route as we don't need it right now.

// app-routing.module.ts
import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  { path: '', redirectTo: 'student-list', pathMatch: 'full' },
  {
    path: 'student-create',
    loadChildren: () => import('./student-create/student-create.module').then(m => m.StudentCreatePageModule)
  },
  {
    path: 'student-edit',
    loadChildren: () => import('./student-edit/student-edit.module').then(m => m.StudentEditPageModule)
  },
  {
    path: 'student-list',
    loadChildren: () => import('./student-list/student-list.module').then(m => m.StudentListPageModule)
  },
  {
    path: 'student-detail',
    loadChildren: () => import('./student-detail/student-detail.module').then(m => m.StudentDetailPageModule)
  },
];



@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Add HttpClientModule in Application

For making HTTP calls to the server we need to import HttpClientModule in application's main module file.

Update the app.module.ts file with the following code:

//app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    BrowserModule, 
    IonicModule.forRoot(), 
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [
    StatusBar,
    SplashScreen,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

HttpConfig Service with Methods for CRUD Operations

Next, we will create a new service which will have methods for making server HTTP calls for CRUD operations.

In this service, we will also import HttpHeader and HttpErrorResponse class for manipulating HTTP headers and handle errors.

Now create a new service file named api.service.ts under services folder by running following ng generate command:

$ ng generate service services/api

For getting JSON response from the server we need to set the 'Content-Type' of every with 'application/json'

//api.service.ts
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { Student } from '../models/student';
import { Observable, throwError } from 'rxjs';
import { retry, catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class ApiService {

  // API path
  base_path = 'http://localhost:3000/students';

  constructor(private http: HttpClient) { }

  // Http Options
  httpOptions = {
    headers: new HttpHeaders({
      'Content-Type': 'application/json'
    })
  }

  // Handle API errors
  handleError(error: HttpErrorResponse) {
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error('An error occurred:', error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      console.error(
        `Backend returned code ${error.status}, ` +
        `body was: ${error.error}`);
    }
    // return an observable with a user-facing error message
    return throwError(
      'Something bad happened; please try again later.');
  };


  // Create a new item
  createItem(item): Observable<Student> {
    return this.http
      .post<Student>(this.base_path, JSON.stringify(item), this.httpOptions)
      .pipe(
        retry(2),
        catchError(this.handleError)
      )
  }

  // Get single student data by ID
  getItem(id): Observable<Student> {
    return this.http
      .get<Student>(this.base_path + '/' + id)
      .pipe(
        retry(2),
        catchError(this.handleError)
      )
  }

  // Get students data
  getList(): Observable<Student> {
    return this.http
      .get<Student>(this.base_path)
      .pipe(
        retry(2),
        catchError(this.handleError)
      )
  }

  // Update item by id
  updateItem(id, item): Observable<Student> {
    return this.http
      .put<Student>(this.base_path + '/' + id, JSON.stringify(item), this.httpOptions)
      .pipe(
        retry(2),
        catchError(this.handleError)
      )
  }

  // Delete item by id
  deleteItem(id) {
    return this.http
      .delete<Student>(this.base_path + '/' + id, this.httpOptions)
      .pipe(
        retry(2),
        catchError(this.handleError)
      )
  }

}

 

Also, create an Interface class for Students data by running following command defining the type of values for student item.

$ ng generate class models/Student

then replace the following content in the newly created file "~/models/student.ts"

export class Student {
   id: number;
   name: string;
   age: string;
   address: string;
}

Mock Server for Application

To create a fully working Application, we will create a local running server by installing json-server package. Run following NPM command to install:

$ npm install -g json-server

Create a new data.json file at the project root "~API/data.json" under Ionic5HttpClient folder and replace the following JSON data in it:

{
  "students": [
  {
    "id": 1,
    "name": "Enola Rowe",
    "class": "[email protected]",
    "address": "131 Oswaldo Street"
  },{
    "id": 2,
    "name": "Timmothy Lueilwitz",
    "age": "15",
    "address": "37137 Abbigail Lock"
  },{
    "id": 3,
    "name": "Madilyn Pacocha",
    "age": "14",
    "address": "094 Morris Plains"
  },{
    "id": 4,
    "name": "Harley Cremin",
    "age": "17",
    "address": "14855 Cathy Square"
  },{
    "id": 5,
    "name": "Juana Ziemann",
    "age": "16",
    "address": "612 Dayana Stream"
  }
  ]
}

You can run server response by running following NPM command:

$ json-server --watch API/data.json

It will return a smiley face \{^_^}/ hi! with the server data path: http://localhost:3000/students

Create New Students

In Create component template we will add a form to take Name, Age, and Address values from the user which will get submitted using submitForm() method.

Update student-create.page.html file with below code:

<!-- student-create.html -->
<ion-header>
  <ion-toolbar color="tertiary">
    <ion-title>Create Student</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content class="ion-padding">

  <ion-item>
    <ion-label>Name</ion-label>
    <ion-input [(ngModel)]="data.name" placeholder="Enter Name"></ion-input>
  </ion-item>

  <ion-item>
    <ion-label>Age</ion-label>
    <ion-input [(ngModel)]="data.age" placeholder="Enter Age"></ion-input>
  </ion-item>

  <ion-item>
    <ion-button (click)="submitForm()">Add
    </ion-button>
  </ion-item>

</ion-content> 

In the student-create.page.ts file, we will have the submitForm() method to call API service method createItem() to return an Observable. After successfully submitting value we will navigate to list page using Router service.

//student-create.page.ts
import { Component, OnInit } from '@angular/core';
import { Student } from '../models/student';
import { ApiService } from '../services/api.service';
import { Router } from '@angular/router';

@Component({
  selector: 'app-student-create',
  templateUrl: './student-create.page.html',
  styleUrls: ['./student-create.page.scss'],
})
export class StudentCreatePage implements OnInit {

  data: Student

  constructor(
    public apiService: ApiService,
    public router: Router
  ) {
    this.data = new Student();
  }

  ngOnInit() {
  }

  submitForm() {
    this.apiService.createItem(this.data).subscribe((response) => {
      this.router.navigate(['student-list']);
    });

  }

}

List Students Component

In list page, we will list all Students in our data.json file in a bootstrap table with rows iterating using *ngFor directive.

The table will also have an Action colum to show Edit and Delete buttons. In Edit action we are simply redirecting to Edit component with item id which we will update in later. The delete button will call delete method in our API service.

Replace the student-list.page.html file with the following code:

<!-- student-list.page.html -->
<ion-header>
  <ion-toolbar color="tertiary">
    <ion-title>List All Students</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content class="ion-padding">
  <table class="table">
    <thead>
      <tr>
        <th scope="col">Id</th>
        <th scope="col">Name</th>
        <th scope="col">Age</th>
        <th scope="col">Address</th>
        <th scope="col">Actions</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let item of studentsData">
        <td>{{ item.id }}</td>
        <td>{{ item.name }}</td>
        <td>{{ item.age }}</td>
        <td>{{ item.address }}</td>
        <td style="display: flex">
          <ion-button color="warning" size="small" routerLink='/student-edit/{{item.id}}'>
            <ion-icon name="create"></ion-icon>
          </ion-button>
          <ion-button color="danger" size="small" (click)="delete(item)">
            <ion-icon name="trash"></ion-icon>
          </ion-button>
        </td>
      </tr>
    </tbody>
  </table>
  <ion-button [routerLink]="['/student-create']">
    Add Student
  </ion-button>
</ion-content>

In the student-list.page.ts file, we will get the list of all students by calling getList() and also add delete()  to call deleteItem() method in API service.

//student-list.page.ts
import { Component, OnInit } from '@angular/core';
import { ApiService } from '../services/api.service';

@Component({
  selector: 'app-student-list',
  templateUrl: './student-list.page.html',
  styleUrls: ['./student-list.page.scss'],
})
export class StudentListPage implements OnInit {

  studentsData: any;

  constructor(
    public apiService: ApiService
  ) {
    this.studentsData = [];
  }

  ngOnInit() {
    // this.getAllStudents();
  }

  ionViewWillEnter() {
    // Used ionViewWillEnter as ngOnInit is not 
    // called due to view persistence in Ionic
    this.getAllStudents();
  }

  getAllStudents() {
    //Get saved list of students
    this.apiService.getList().subscribe(response => {
      console.log(response);
      this.studentsData = response;
    })
  }


  delete(item) {
    //Delete item in Student data
    this.apiService.deleteItem(item.id).subscribe(Response => {
      //Update list after delete is successful
      this.getAllStudents();
    });
  }

}

Update Student Item

In Edit component we will get the id of item using ActivatedRoute service then get its details. After that, we will show Form field controls to edit them, after that user can update the value to call the updateItem() method in API service.

In the student-edit.page.html file replace following HTML content:

<!-- student-edit.page.html -->
<ion-header>
  <ion-toolbar color="tertiary">
    <ion-title>Edit Student Record</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content class="ion-padding">

  <ion-item>
    <ion-label>Name</ion-label>
    <ion-input [(ngModel)]="data.name" placeholder="Enter Name"></ion-input>
  </ion-item>
  <ion-item>
    <ion-label>Age</ion-label>
    <ion-input [(ngModel)]="data.age" placeholder="Enter Age"></ion-input>
  </ion-item>
  <ion-item>
    <ion-label>Address</ion-label>
    <ion-input [(ngModel)]="data.address" placeholder="Enter Address"></ion-input>
  </ion-item>
  <ion-button (click)="update()" color="success" size="small">
    Update
  </ion-button>
  <ion-button [routerLink]="[ '/student-list']" color="danger" size="small">
    Cancel
  </ion-button>
</ion-content>

Now in student-edit.page.ts file replace following class component code:

//student-edit.page
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Student } from '../models/student';
import { ApiService } from '../services/api.service';

@Component({
  selector: 'app-student-edit',
  templateUrl: './student-edit.page.html',
  styleUrls: ['./student-edit.page.scss'],
})
export class StudentEditPage implements OnInit {


  id: number;
  data: Student;

  constructor(
    public activatedRoute: ActivatedRoute,
    public router: Router,
    public apiService: ApiService
  ) {
    this.data = new Student();
  }

  ngOnInit() {
    this.id = this.activatedRoute.snapshot.params["id"];
    //get item details using id
    this.apiService.getItem(this.id).subscribe(response => {
      console.log(response);
      this.data = response;
    })
  }

  update() {
    //Update item by taking id and updated data object
    this.apiService.updateItem(this.id, this.data).subscribe(response => {
      this.router.navigate(['student-list']);
    })
  }

}

That's it now you are ready to run your app by hitting following command

$ ionic serve --open

Don't forget to run the json-server to up API server by running following command in a separate console.

$ json-server --watch API/data.json

If you are using Visual Studio Code terminal then you can hit plus icon to open a new terminal.

Conclusion: So here we create a demo application to show how to communicate with the server to consume RESTful API. We also created a Mock server with the help of awsome json-server npm package.

10 thoughts on “Ionic 5|4 HttpClient CRUD Service Tutorial to Consume RESTful Server API

Leave a Reply

Your email address will not be published. Required fields are marked *