In this tutorial, we show you Angular 8 Http Client & Spring Boot Server example that uses Spring JPA to do CRUD with MySQL and Angular 8 as a front-end technology to make HTTP request and receive response.
Related Post:
– How to use Spring JPA MySQL | Spring Boot
– How to install Angular 8 locally
Technologies
– Java 8
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.4.RELEASE
– Spring Boot: 2.1.3.RELEASE
– Angular 8
– RxJS 6
Video
Spring Boot + Angular 8 + MySQL example Overview
Spring Boot RestApis Server

Our Spring Boot Server can work with MySQL Database and provides APIs:
- GET
/customers/
: get all customers - GET
/customers/[id]
: get a customer byid
- GET
/customers/age/[age]
: find all customers byage
- POST
/customers/
: save a customer - PUT
/customers/[id]
: update a customer byid
- DELETE
/customers/[id]
: delete a customer byid
- DELETE
/customers/
: delete all customers
Angular 8 Client
The image below shows overview about Angular Components that we will create:

Implement Spring Boot Server
Project Structure

– Customer class corresponds to entity and table customer.
– CustomerRepository is an interface extends CrudRepository, will be autowired in CustomerController for implementing repository methods and custom finder methods.
– CustomerController is a REST Controller which has request mapping methods for RESTful requests such as: getAllCustomers
, postCustomer
, deleteCustomer
, deleteAllCustomers
, findByAge
, updateCustomer
.
– Configuration for Spring Datasource and Spring JPA properties in application.properties
– Dependencies for Spring Boot and MySQL in pom.xml
We will keep the tutorial as simple as possible, so for post with Spring JPA details, you can visit:
How to use Spring JPA MySQL | Spring Boot
Dependencies
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
Data Model
model/Customer.java
package com.ozenero.spring.restapi.mysql.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "customer") public class Customer { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @Column(name = "name") private String name; @Column(name = "age") private int age; @Column(name = "active") private boolean active; public Customer() { } public Customer(String name, int age) { this.name = name; this.age = age; this.active = false; } public long getId() { return id; } public void setName(String name) { this.name = name; } public String getName() { return this.name; } public void setAge(int age) { this.age = age; } public int getAge() { return this.age; } public boolean isActive() { return active; } public void setActive(boolean active) { this.active = active; } @Override public String toString() { return "Customer [id=" + id + ", name=" + name + ", age=" + age + ", active=" + active + "]"; } }
JPA Repository
repo/CustomerRepository.java
package com.ozenero.spring.restapi.mysql.repo; import java.util.List; import org.springframework.data.repository.CrudRepository; import com.ozenero.spring.restapi.mysql.model.Customer; public interface CustomerRepository extends CrudRepository{ List findByAge(int age); }
REST Controller
controller/CustomerController.javapackage com.ozenero.spring.restapi.mysql.controller; import java.util.ArrayList; import java.util.List; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.ozenero.spring.restapi.mysql.model.Customer; import com.ozenero.spring.restapi.mysql.repo.CustomerRepository; @CrossOrigin(origins = "http://localhost:4200") @RestController @RequestMapping("/api") public class CustomerController { @Autowired CustomerRepository repository; @GetMapping("/customers") public ResponseEntity> getAllCustomers() { List
customers = new ArrayList<>(); try { repository.findAll().forEach(customers::add); if (customers.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } return new ResponseEntity<>(customers, HttpStatus.OK); } catch (Exception e) { return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR); } } @GetMapping("/customers/{id}") public ResponseEntity getCustomerById(@PathVariable("id") long id) { Optional customerData = repository.findById(id); if (customerData.isPresent()) { return new ResponseEntity<>(customerData.get(), HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } } @PostMapping(value = "/customers") public ResponseEntity postCustomer(@RequestBody Customer customer) { try { Customer _customer = repository.save(new Customer(customer.getName(), customer.getAge())); return new ResponseEntity<>(_customer, HttpStatus.CREATED); } catch (Exception e) { return new ResponseEntity<>(null, HttpStatus.EXPECTATION_FAILED); } } @DeleteMapping("/customers/{id}") public ResponseEntity deleteCustomer(@PathVariable("id") long id) { try { repository.deleteById(id); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } catch (Exception e) { return new ResponseEntity<>(HttpStatus.EXPECTATION_FAILED); } } @DeleteMapping("/customers") public ResponseEntity deleteAllCustomers() { try { repository.deleteAll(); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } catch (Exception e) { return new ResponseEntity<>(HttpStatus.EXPECTATION_FAILED); } } @GetMapping(value = "customers/age/{age}") public ResponseEntity > findByAge(@PathVariable int age) { try { List
customers = repository.findByAge(age); if (customers.isEmpty()) { return new ResponseEntity<>(HttpStatus.NO_CONTENT); } return new ResponseEntity<>(customers, HttpStatus.OK); } catch (Exception e) { return new ResponseEntity<>(HttpStatus.EXPECTATION_FAILED); } } @PutMapping("/customers/{id}") public ResponseEntity updateCustomer(@PathVariable("id") long id, @RequestBody Customer customer) { Optional customerData = repository.findById(id); if (customerData.isPresent()) { Customer _customer = customerData.get(); _customer.setName(customer.getName()); _customer.setAge(customer.getAge()); _customer.setActive(customer.isActive()); return new ResponseEntity<>(repository.save(_customer), HttpStatus.OK); } else { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } } }
Configuration for Spring Datasource & JPA properties
application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/testdb?useSSL=false spring.datasource.username=root spring.datasource.password=123456 spring.jpa.generate-ddl=true
Implement Angular 8 HTTP Client
Project Structure

We have:
– 4 components: customers-list, customer-details, create-customer, search-customer.
– 3 modules: FormsModule, HttpClientModule, AppRoutingModule.
– customer.ts: class Customer (id, firstName, lastName)
– customer.service.ts: Service for Http Client methods
– app-routing.module.ts: Routing configuration
Create Service & Components
On Project folder, run commands below:
– ng g s customer
– ng g c create-customer
– ng g c customer-details
– ng g c customers-list
– ng g c search-customers
App Module
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'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { CreateCustomerComponent } from './create-customer/create-customer.component'; import { CustomerDetailsComponent } from './customer-details/customer-details.component'; import { CustomersListComponent } from './customers-list/customers-list.component'; import { SearchCustomersComponent } from './search-customers/search-customers.component'; @NgModule({ declarations: [ AppComponent, CreateCustomerComponent, CustomerDetailsComponent, CustomersListComponent, SearchCustomersComponent ], imports: [ BrowserModule, AppRoutingModule, FormsModule, HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Add Router
app-routing.module.ts
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { CustomersListComponent } from './customers-list/customers-list.component'; import { CreateCustomerComponent } from './create-customer/create-customer.component'; import { SearchCustomersComponent } from './search-customers/search-customers.component'; const routes: Routes = [ { path: '', redirectTo: 'customer', pathMatch: 'full' }, { path: 'customer', component: CustomersListComponent }, { path: 'add', component: CreateCustomerComponent }, { path: 'findbyage', component: SearchCustomersComponent }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
AppComponent HTML for routing:
app.component.html
<div style="padding: 20px;"> <h1 style="color: blue">ozenero.com</h1> <h3>{{title}}</h3> <nav> <a routerLink="customer" class="btn btn-primary active" role="button" routerLinkActive="active">Customers</a> <a routerLink="add" class="btn btn-primary active" role="button" routerLinkActive="active">Add</a> <a routerLink="findbyage" class="btn btn-primary active" role="button" routerLinkActive="active">Search</a> </nav> <router-outlet></router-outlet> </div>
Data Model
Create new file named customer.ts:
export class Customer { id: number; name: string; age: number; active: boolean; }
Data Service
customer.service.ts
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class CustomerService { private baseUrl = 'http://localhost:8080/api/customers'; constructor(private http: HttpClient) { } getCustomer(id: number): Observable{ return this.http.get(`${this.baseUrl}/${id}`); } createCustomer(customer: any): Observable { return this.http.post(this.baseUrl, customer); } updateCustomer(id: number, value: any): Observable { return this.http.put(`${this.baseUrl}/${id}`, value); } deleteCustomer(id: number): Observable { return this.http.delete(`${this.baseUrl}/${id}`); } getCustomersList(): Observable { return this.http.get(this.baseUrl); } getCustomersByAge(age: number): Observable { return this.http.get(`${this.baseUrl}/age/${age}`); } deleteAll(): Observable { return this.http.delete(this.baseUrl); } }
Components
List of Customers
customers-list/customers-list.component.ts
import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { CustomerService } from '../customer.service'; import { Customer } from '../customer'; @Component({ selector: 'app-customers-list', templateUrl: './customers-list.component.html', styleUrls: ['./customers-list.component.css'] }) export class CustomersListComponent implements OnInit { customers: Observable; constructor(private customerService: CustomerService) { } ngOnInit() { this.reloadData(); } deleteCustomers() { this.customerService.deleteAll() .subscribe( data => { console.log(data); this.reloadData(); }, error => console.log('ERROR: ' + error)); } reloadData() { this.customers = this.customerService.getCustomersList(); } }
customers-list/customers-list.component.html
<h3>Customers</h3> <div *ngFor="let customer of customers | async" style="width: 300px;"> <app-customer-details [customer]='customer'></app-customer-details> </div> <div> <button type="button" class="button btn-danger" (click)='deleteCustomers()'>Delete All</button> </div>
Customer Details
customer-details/customer-details.component.ts
import { Component, OnInit, Input } from '@angular/core'; import { CustomerService } from '../customer.service'; import { Customer } from '../customer'; import { CustomersListComponent } from '../customers-list/customers-list.component'; @Component({ selector: 'app-customer-details', templateUrl: './customer-details.component.html', styleUrls: ['./customer-details.component.css'] }) export class CustomerDetailsComponent implements OnInit { @Input() customer: Customer; constructor(private customerService: CustomerService, private listComponent: CustomersListComponent) { } ngOnInit() { } updateActive(isActive: boolean) { this.customerService.updateCustomer(this.customer.id, { name: this.customer.name, age: this.customer.age, active: isActive }) .subscribe( data => { console.log(data); this.customer = data as Customer; }, error => console.log(error)); } deleteCustomer() { this.customerService.deleteCustomer(this.customer.id) .subscribe( data => { console.log(data); this.listComponent.reloadData(); }, error => console.log(error)); } }
customer-details/customer-details.component.html
<div *ngIf="customer"> <div> <label>Name: </label> {{customer.name}} </div> <div> <label>Age: </label> {{customer.age}} </div> <div> <label>Active: </label> {{customer.active}} </div> <span class="button is-small btn-primary" *ngIf='customer.active' (click)='updateActive(false)'>Inactive</span> <span class="button is-small btn-primary" *ngIf='!customer.active' (click)='updateActive(true)'>Active</span> <span class="button is-small btn-danger" (click)='deleteCustomer()'>Delete</span> <hr /> </div>
Create Customer
create-customer/create-customer.component.ts
import { Component, OnInit } from '@angular/core'; import { Customer } from '../customer'; import { CustomerService } from '../customer.service'; @Component({ selector: 'app-create-customer', templateUrl: './create-customer.component.html', styleUrls: ['./create-customer.component.css'] }) export class CreateCustomerComponent implements OnInit { customer: Customer = new Customer(); submitted = false; constructor(private customerService: CustomerService) { } ngOnInit() { } newCustomer(): void { this.submitted = false; this.customer = new Customer(); } save() { this.customerService.createCustomer(this.customer) .subscribe( data => { console.log(data); this.submitted = true; }, error => console.log(error)); this.customer = new Customer(); } onSubmit() { this.save(); } }
create-customer/create-customer.component.html
<h3>Create Customer</h3> <div [hidden]="submitted" style="width: 300px;"> <form (ngSubmit)="onSubmit()"> <div class="form-group"> <label for="name">Name</label> <input type="text" class="form-control" id="name" required [(ngModel)]="customer.name" name="name"> </div> <div class="form-group"> <label for="age">Age</label> <input type="text" class="form-control" id="age" required [(ngModel)]="customer.age" name="age"> </div> <button type="submit" class="btn btn-success">Submit</button> </form> </div> <div [hidden]="!submitted"> <h4>You submitted successfully!</h4> <button class="btn btn-success" (click)="newCustomer()">Add</button> </div>
Search Customers
search-customers/search-customers.component.ts
import { Component, OnInit } from '@angular/core'; import { Customer } from '../customer'; import { CustomerService } from '../customer.service'; @Component({ selector: 'app-search-customers', templateUrl: './search-customers.component.html', styleUrls: ['./search-customers.component.css'] }) export class SearchCustomersComponent implements OnInit { age: number; customers: Customer[]; constructor(private dataService: CustomerService) { } ngOnInit() { this.age = 0; } private searchCustomers() { this.customers = []; this.dataService.getCustomersByAge(this.age) .subscribe(customers => this.customers = customers); } onSubmit() { this.searchCustomers(); } }
search-customers/search-customers.component.html
<h3>Find By Age</h3> <div style="width: 300px;"> <form (ngSubmit)="onSubmit()"> <div class="form-group"> <label for="lastname">Age</label> <input type="text" class="form-control" id="age" required [(ngModel)]="age" name="age"> </div> <div class="btn-group"> <button type="submit" class="btn btn-success">Submit</button> </div> </form> </div> <ul> <li *ngFor="let customer of customers"> <h4>{{customer.id}} - {{customer.name}} {{customer.age}}</h4> </li> </ul>
Run & Check Results
– Build and Run Spring Boot project with commandlines: mvn clean install
and mvn spring-boot:run
.
– Run the Angular App with command: ng serve
.
– Open browser for url http://localhost:4200/
Add Customer:

Show Customers:

Click on Active button to update Customer status:

Search Customers by Age:

Delete a Customer:

Delete All Customers:

Source Code
– server-SpringBootRestApiMySQL
– client-Angular8SpringBoot