Angular 4 + Spring JPA + PostgreSQL example | Angular 4 Http Client – Spring Boot RestApi Server

In this tutorial, ozenero shows you Angular 4 Http Client & Spring Boot Server example that uses Spring JPA to interact with PostgreSQL and Angular 4 as a front-end technology to make request and receive response.

Related Posts:
How to use Spring JPA with PostgreSQL | Spring Boot
Spring JPA + PostgreSQL + AngularJS example | Spring Boot
How to use Angular Http Client to fetch Data from SpringBoot RestAPI – Angular 4
How to use Angular HttpClient to POST, PUT, DELETE data on SpringBoot Rest APIs – Angular 4

Spring Boot + Angular 6 example | Spring Data JPA + REST + PostgreSQL CRUD example

I. Technologies

– Java 1.8
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.4.RELEASE
– Spring Boot: RELEASE
– Angular 4

II. Overview


1. Spring Boot Server


For more details about Spring JPA – PostgreSQL, please visit:
How to use Spring JPA with PostgreSQL | Spring Boot

2. Angular 4 Client


For more details:
– About Angular 4 Routing:
How to work with Angular Routing – Spring Boot + Angular 4
– About Angular Http Client to GET/POST/DELETE:
+ How to use Angular Http Client to fetch Data from SpringBoot RestAPI – Angular 4
+ How to use Angular HttpClient to POST, PUT, DELETE data on SpringBoot Rest APIs – Angular 4

III. Practice

1. Project Structure

1.1 Spring Boot Server


– Class Customer corresponds to entity and table customer, it should be implemented Serializable.
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: getAll, postCustomer, delete, findByLastName.
– Configuration for Spring Datasource and Spring JPA properties in
Dependencies for Spring Boot and PostgreSQL in pom.xml

1.2 Angular 4 Client


In this example, we focus on:
– 4 components: customers, customer-details, create-customer, search-customer.
– 3 modules: FormsModule, HttpModule, AppRoutingModule.
customer.ts: class Customer (id, firstName, lastName)
data.service.ts: DataService for Http Client methods
proxy.conf.json for integrating Angular Client with Spring Boot Server.

2. How to do

2.1 Spring Boot Server

2.1.1 Dependency


2.1.2 Customer – Data Model

package com.javasampleapproach.jpapostgresqlangular4.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;

@Table(name = "customer")
public class Customer implements Serializable {

	private static final long serialVersionUID = -3009157732242241606L;
	@GeneratedValue(strategy = GenerationType.AUTO)
	private long id;

	@Column(name = "firstname")
	private String firstName;

	@Column(name = "lastname")
	private String lastName;

	protected Customer() {

	public long getId() {
		return id;

	public void setId(long id) { = id;

	public String getFirstName() {
		return firstName;

	public void setFirstName(String firstName) {
		this.firstName = firstName;

	public String getLastName() {
		return lastName;

	public void setLastName(String lastName) {
		this.lastName = lastName;

	public Customer(String firstName, String lastName) {
		this.firstName = firstName;
		this.lastName = lastName;

	public String toString() {
		return String.format("Customer[id=%d, firstName='%s', lastName='%s']", id, firstName, lastName);
2.1.3 JPA Repository

package com.javasampleapproach.jpapostgresqlangular4.repo;

import java.util.List;


import com.javasampleapproach.jpapostgresqlangular4.model.Customer;

public interface CustomerRepository extends CrudRepository {
	List findByLastName(String lastName);
2.1.4 REST Controller

package com.javasampleapproach.jpapostgresqlangular4.controller;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
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.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.javasampleapproach.jpapostgresqlangular4.model.Customer;
import com.javasampleapproach.jpapostgresqlangular4.repo.CustomerRepository;

public class CustomerController {

	CustomerRepository repository;

	@GetMapping(value="/customer",  produces=MediaType.APPLICATION_JSON_VALUE)
	public List getAll() {
		List list = new ArrayList<>();
		Iterable customers = repository.findAll();

		return list;
	public Customer postCustomer(@RequestBody Customer customer) { Customer(customer.getFirstName(), customer.getLastName()));
		return customer;

	@GetMapping(value="/findbylastname/{lastName}",  produces=MediaType.APPLICATION_JSON_VALUE)
	public List findByLastName(@PathVariable String lastName) {

		List customers = repository.findByLastName(lastName);
		return customers;
	public void deleteCustomer(@PathVariable long id){
2.1.5 Configuration for Spring Datasource & JPA properties


2.2 Angular 4 Client

2.2.0 Model

export class Customer {
    public id: number;
    public firstName: string;
    public lastName: string;
2.2.1 DataService

import { Injectable } from '@angular/core';
import { Headers, Http } from '@angular/http';

import 'rxjs/add/operator/toPromise';

import { Customer } from './customer';

export class DataService {

  private customersUrl = 'customer';  // URL to web API
  private headers = new Headers({'Content-Type': 'application/json'});

  constructor(private http: Http) {}

  // Get all customers
  getCustomers(): Promise {
    return this.http.get(this.customersUrl)
      .then(response => response.json() as Customer[])

  getCustomersByLastName(lastName: string): Promise {
    const url = `findbylastname/${lastName}`;
    return this.http.get(url)
      .then(response => response.json() as Customer)

  create(customer: Customer): Promise {
    return this.http
      .post("postcustomer", JSON.stringify(customer), {headers : this.headers})
      .then(res => res.json() as Customer)

  delete(id: number): Promise {
    const url = `${this.customersUrl}/${id}`;
    return this.http.delete(url, {headers: this.headers})
      .then(() => null)

  private handleError(error: any): Promise {
    console.error('Error', error); // for demo purposes only
    return Promise.reject(error.message || error);
2.2.2 Components

– CustomersComponent:

import { Component, OnInit } from '@angular/core';
import { Customer } from '../customer';
import { DataService } from '../data.service';

  selector: 'customers-list',
  templateUrl: './customers.component.html',
  styleUrls: ['./customers.component.css'],

export class CustomersComponent implements OnInit {
  customers: Customer[];
  selectedCustomer: Customer;

  constructor(private dataService: DataService) {}

  getCustomers() {
     this.dataService.getCustomers().then(customers => this.customers = customers);

  ngOnInit(): void {

  onSelect(cust: Customer): void {
    this.selectedCustomer = cust;
    <li *ngFor="let cust of customers"
		[class.selected]="cust === selectedCustomer" (click)="onSelect(cust)">
		<h4>{{}} - {{cust.firstName}} {{cust.lastName}}</h4> 
<customer-detail [customer]="selectedCustomer"></customer-detail>

– CustomerDetailsComponent:

import { Component, Input } from '@angular/core';

import { Customer } from '../customer';
import { DataService } from '../data.service';

  selector: 'customer-detail',
  templateUrl: './customer-details.component.html',
  styleUrls: ['./customer-details.component.css'],
  providers: [DataService]

export class CustomerDetailsComponent {

  @Input() customer: Customer;

  constructor(private dataService: DataService) {}

  delete(): void {
    this.dataService.delete( => this.goBack());

  goBack(): void {
<div *ngIf="customer">
	<h2>{{customer.firstName}} details:</h2>
		<label>id: </label> {{}}
		<label>First Name: </label> {{customer.firstName}}
		<label>Last Name: </label> {{customer.lastName}}
	<button class="btn btn-danger" (click)="delete()">Delete</button>

– CreateCustomerComponent:

import {Customer} from '../customer';
import {DataService} from '../data.service';
import {Component, OnInit} from '@angular/core';
import {Location} from '@angular/common';

  selector: 'app-create-customer',
  templateUrl: './create-customer.component.html',
  styleUrls: ['./create-customer.component.css']

export class CreateCustomerComponent implements OnInit {
  customer = new Customer;
  submitted = false;
  constructor(private dataService: DataService,
    private location: Location) {}

  ngOnInit() {

  newCustomer(): void {
    this.submitted = false;
    this.customer = new Customer();

  private save(): void {

  onSubmit() {
    this.submitted = true;;

  goBack(): void {
<h3>Create Customer Form</h3>
<div [hidden]="submitted" style="width: 300px;">
	<form (ngSubmit)="onSubmit()">
		<div class="form-group">
			<label for="firstname">First Name</label> <input type="text"
				class="form-control" id="firstname" required
				[(ngModel)]="customer.firstName" name="firstname">

		<div class="form-group">
			<label for="lastname">Last Name</label> <input type="text"
				class="form-control" id="lastname" required
				[(ngModel)]="customer.lastName" name="lastname">

		<div class="btn-group">
			<button class="btn btn-primary" (click)="goBack()">Back</button>
			<button type="submit" class="btn btn-success">Submit</button>

<div [hidden]="!submitted">
	<div class="btn-group ">
		<h4>You submitted successfully!</h4>
		<button class="btn btn-primary" (click)="goBack()">Back</button>
		<button class="btn btn-success" (click)="newCustomer()">Add</button>

– SearchCustomersComponent:

import {Component, OnInit} from '@angular/core';
import {Customer} from '../customer';
import {DataService} from '../data.service';

  selector: 'app-search-customers',
  templateUrl: './search-customers.component.html',
  styleUrls: ['./search-customers.component.css']

export class SearchCustomersComponent implements OnInit {
  lastName: string;
  customers: Customer[];
  constructor(private dataService: DataService) {}

  ngOnInit() {
    this.lastName = "";

  private searchCustomers() {
    this.dataService.getCustomersByLastName(this.lastName).then(customers => this.customers = customers);

  onSubmit() {

<h3>Find By Last Name</h3>
<div style="width: 300px;">
	<form (ngSubmit)="onSubmit()">
		<div class="form-group">
			<label for="lastname">Last Name</label> <input type="text"
				class="form-control" id="lastname" required [(ngModel)]="lastName"

		<div class="btn-group">
			<button type="submit" class="btn btn-success">Submit</button>
	<li *ngFor="let cust of customers">
		<h4>{{}} - {{cust.firstName}} {{cust.lastName}}</h4>
2.2.3 AppRoutingModule

import {CreateCustomerComponent} from './create-customer/create-customer.component';
import {CustomersComponent} from './customers/customers.component';
import {SearchCustomersComponent} from './search-customers/search-customers.component';

import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';

const routes: Routes = [
  {path: '', redirectTo: 'customer', pathMatch: 'full'},
  {path: 'customer', component: CustomersComponent},
  {path: 'add', component: CreateCustomerComponent},
  {path: 'findbylastname', component: SearchCustomersComponent},

  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]

export class AppRoutingModule {}

And AppComponent HTML for routing:

<div style="padding: 20px;">
	<h2 style="color: blue">JSA - Angular Application!</h2>
		<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="findbylastname"	class="btn btn-primary active" role="button" routerLinkActive="active">Search</a>
2.2.4 AppModule

import {AppRoutingModule} from './app-routing.module';
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {HttpModule} from '@angular/http';

import {AppComponent} from './app.component';
import {CustomerDetailsComponent} from './customer-details/customer-details.component';
import {CustomersComponent} from './customers/customers.component';
import {DataService} from './data.service';
import {CreateCustomerComponent} from './create-customer/create-customer.component';

import {enableProdMode} from '@angular/core';
import {SearchCustomersComponent} from './search-customers/search-customers.component';

  declarations: [
  imports: [
  providers: [DataService],
  bootstrap: [AppComponent]

export class AppModule {}
2.2.5 Integrate Angular Client with Spring Boot Server

– Add proxy.conf.json to root folder of the project:

    "/": {
        "target": "http://localhost:8080",
        "secure": false

– Edit package.json file for “start” script:

"scripts": {
    "ng": "ng",
    "start": "ng serve --proxy-config proxy.conf.json",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"

3. Run & Check Result

Build and Run Spring Boot project with commandlines: mvn clean install and mvn spring-boot:run.
– Run the Angular App with command: npm start.

– Open browser for url http://localhost:4200/:
Add Customer:

Show Customers & click on any Customer:

>> PostgreSQL DB:

Search Customer:

Go back to ShowCustomers, chose a Customer and click on Delete Customer:

>> PostgreSQL DB after deleting:

IV. Source Code


15 thoughts on “Angular 4 + Spring JPA + PostgreSQL example | Angular 4 Http Client – Spring Boot RestApi Server”

  1. I have error line in customer-details.component.ts on line @Input() customer: Customer;

    I just copy yours. I don’t know what’s going on. Please help me out.

      1. CustomerDetailsComponent.html:5 ERROR TypeError: Cannot read property ‘firstName’ of undefined
        at Object.eval [as updateDirectives] (CustomerDetailsComponent.html:7)
        at Object.debugUpdateDirectives [as updateDirectives] (core.es5.js:13067)
        at checkAndUpdateView (core.es5.js:12251)
        at callViewAction (core.es5.js:12599)
        at execComponentViewsAction (core.es5.js:12531)
        at checkAndUpdateView (core.es5.js:12257)
        at callViewAction (core.es5.js:12599)
        at execComponentViewsAction (core.es5.js:12531)
        at checkAndUpdateView (core.es5.js:12257)
        at callViewAction (core.es5.js:12599)

        1. For temp solution
          I have used *ngIf in customer details component html file.

          Create Customer Form


          it is a kinda of validator checks whether customer is undefined or has value.

  2. Hi JSA, love these tutorials, best I find online!

    Can you please make similar tutorial for using browser and Angular 4 and connecting to FTP server , select and read unprocessed CSV file from folder_one, perform simple process( example UpperCase) and save processed CSV to folder_two on FTP server. With browser interaction: User can see unprocess files in folder_one and select file to process / download. Also can see process files in folder_two and select file to download?

    Many Thanks

  3. Please add css files. You added [class.selected] code in the customer.compoenet.html file. But, you didn’t provide css code. That’s why there is an error.

    1. Hi Su,

      [] is special Angular4 binding syntax. For css file, we use Bootstrap in index.html.
      You can download source code for details 🙂


  4. Hi,

    I am facing an issue while trying to use the above code. Attaching the error log of the browser console.

    zone.js:2935 GET http://localhost:4200/customer 404 (Not Found)
    scheduleTask	@	zone.js:2935
    webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.scheduleTask	@	zone.js:407
    onScheduleTask	@	zone.js:297
    webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.scheduleTask	@	zone.js:401
    webpackJsonp.../../../../zone.js/dist/zone.js.Zone.scheduleTask	@	zone.js:232
    webpackJsonp.../../../../zone.js/dist/zone.js.Zone.scheduleMacroTask	@	zone.js:255
    scheduleMacroTaskWithCurrentZone	@	zone.js:1092
    (anonymous)	@	zone.js:2967
    proto.(anonymous function)	@	zone.js:1372
    (anonymous)	@	http.js:2366
    webpackJsonp.../../../../rxjs/_esm5/Observable.js.Observable._trySubscribe	@	Observable.js:172
    webpackJsonp.../../../../rxjs/_esm5/Observable.js.Observable.subscribe	@	Observable.js:160
    subscribeToResult	@	subscribeToResult.js:23
    webpackJsonp.../../../../rxjs/_esm5/operators/mergeMap.js.MergeMapSubscriber._innerSub	@	mergeMap.js:138
    webpackJsonp.../../../../rxjs/_esm5/operators/mergeMap.js.MergeMapSubscriber._tryNext	@	mergeMap.js:135
    webpackJsonp.../../../../rxjs/_esm5/operators/mergeMap.js.MergeMapSubscriber._next	@	mergeMap.js:118
    webpackJsonp.../../../../rxjs/_esm5/	@	Subscriber.js:92
    webpackJsonp.../../../../rxjs/_esm5/observable/ScalarObservable.js.ScalarObservable._subscribe	@	ScalarObservable.js:51
    webpackJsonp.../../../../rxjs/_esm5/Observable.js.Observable._trySubscribe	@	Observable.js:172
    webpackJsonp.../../../../rxjs/_esm5/Observable.js.Observable.subscribe	@	Observable.js:160
    webpackJsonp.../../../../rxjs/_esm5/operators/	@	mergeMap.js:92
    webpackJsonp.../../../../rxjs/_esm5/Observable.js.Observable.subscribe	@	Observable.js:157
    webpackJsonp.../../../../rxjs/_esm5/operators/	@	filter.js:61
    webpackJsonp.../../../../rxjs/_esm5/Observable.js.Observable.subscribe	@	Observable.js:157
    webpackJsonp.../../../../rxjs/_esm5/operators/	@	map.js:57
    webpackJsonp.../../../../rxjs/_esm5/Observable.js.Observable.subscribe	@	Observable.js:157
    (anonymous)	@	Observable.js:286
    ZoneAwarePromise	@	zone.js:875
    webpackJsonp.../../../../rxjs/_esm5/Observable.js.Observable.toPromise	@	Observable.js:284
    webpackJsonp.../../../../../src/app/data.service.ts.DataService.getCustomers	@	data.service.ts:20
    webpackJsonp.../../../../../src/app/customers/customers.component.ts.CustomersComponent.getCustomers	@	customers.component.ts:18
    webpackJsonp.../../../../../src/app/customers/customers.component.ts.CustomersComponent.ngOnInit	@	customers.component.ts:22
    checkAndUpdateDirectiveInline	@	core.js:12364
    checkAndUpdateNodeInline	@	core.js:13888
    checkAndUpdateNode	@	core.js:13831
    debugCheckAndUpdateNode	@	core.js:14724
    debugCheckDirectivesFn	@	core.js:14665
    (anonymous)	@	CustomersComponent_H…gfactory.js? [sm]:1
    debugUpdateDirectives	@	core.js:14650
    checkAndUpdateView	@	core.js:13797
    callViewAction	@	core.js:14148
    execEmbeddedViewsAction	@	core.js:14106
    checkAndUpdateView	@	core.js:13798
    callViewAction	@	core.js:14148
    execComponentViewsAction	@	core.js:14080
    checkAndUpdateView	@	core.js:13803
    callWithDebugContext	@	core.js:15051
    debugCheckAndUpdateView	@	core.js:14588
    webpackJsonp.../../../core/esm5/core.js.ViewRef_.detectChanges	@	core.js:11572
    (anonymous)	@	core.js:5903
    webpackJsonp.../../../core/esm5/core.js.ApplicationRef.tick	@	core.js:5903
    (anonymous)	@	core.js:5736
    webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke	@	zone.js:388
    onInvoke	@	core.js:4745
    webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke	@	zone.js:387
    webpackJsonp.../../../../zone.js/dist/	@	zone.js:138
    webpackJsonp.../../../core/esm5/	@	core.js:4562
    next	@	core.js:5736
    schedulerFn	@	core.js:4327
    webpackJsonp.../../../../rxjs/_esm5/Subscriber.js.SafeSubscriber.__tryOrUnsub	@	Subscriber.js:240
    webpackJsonp.../../../../rxjs/_esm5/	@	Subscriber.js:187
    webpackJsonp.../../../../rxjs/_esm5/Subscriber.js.Subscriber._next	@	Subscriber.js:128
    webpackJsonp.../../../../rxjs/_esm5/	@	Subscriber.js:92
    webpackJsonp.../../../../rxjs/_esm5/	@	Subject.js:56
    webpackJsonp.../../../core/esm5/core.js.EventEmitter.emit	@	core.js:4307
    checkStable	@	core.js:4710
    onHasTask	@	core.js:4758
    webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.hasTask	@	zone.js:441
    webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate._updateTaskCount	@	zone.js:461
    webpackJsonp.../../../../zone.js/dist/zone.js.Zone._updateTaskCount	@	zone.js:285
    webpackJsonp.../../../../zone.js/dist/zone.js.Zone.runTask	@	zone.js:205
    drainMicroTaskQueue	@	zone.js:595
    Promise.then (async)		
    scheduleMicroTask	@	zone.js:578
    webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.scheduleTask	@	zone.js:410
    webpackJsonp.../../../../zone.js/dist/zone.js.Zone.scheduleTask	@	zone.js:232
    webpackJsonp.../../../../zone.js/dist/zone.js.Zone.scheduleMicroTask	@	zone.js:252
    scheduleResolveOrReject	@	zone.js:856
    ZoneAwarePromise.then	@	zone.js:946
    webpackJsonp.../../../core/esm5/core.js.PlatformRef.bootstrapModule	@	core.js:5564
    ../../../../../src/main.ts	@	main.ts:11
    __webpack_require__	@	bootstrap 8615279…:54
    0	@	main.bundle.js:621
    __webpack_require__	@	bootstrap 8615279…:54
    webpackJsonpCallback	@	bootstrap 8615279…:25
    1. kindly run your code by using following command “ng serve –proxy-config proxy.conf.json” .

Leave a Reply

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