Node.js/Express RestAPIs – Angular 6 HttpClient – Get/Post/Put/Delete requests + Bootstrap 4

Node.js/Express RestAPIs – Angular 6 HttpClient – Get/Post/Put/Delete requests + Bootstrap 4

Angular provides the HttpClient in @angular/common/http for front-end applications communicate with backend services. In the tutorial, we show how to build an Angular application that uses the HttpClient to make get/post/put/delete requests with Observable apis to Node.js RestAPIs.

Related posts:
Angular 6 Service – with Observable Data for Asynchronous Operation
Angular 6 Routing/Navigation – with Angular Router Service
Angular 6 Template Driven Form – NgModel for Two-Way Data Binding

Technologies

  • Angular 6
  • RxJS 6
  • Bootstrap 4
  • Visual Studio Code – version 1.24.0
  • Nodejs – v8.11.3

Overview

Goal

We create 2 projects:

– Angular Client Project:

angular-6-http-client-node.js-rest-apis-post-get-put-delete +angular-project-structure

– Node.js RestAPIs project:

angular-6-http-client-node.js-rest-apis-post-get-put-delete + nodejs-restapi-project-structure

UserCase

– Retrieve all customers from Node.js RestAPIs:

angular-6-http-client-node.js-rest-apis-post-get-put-delete + retrieve-all-datas

– Update a customer -> Change the firstname of first customer: ‘Joe’ to ‘Robert’.

angular-6-http-client-get-post-put-delete-reques-to-nodejs-restapi-upadate-joe-to-robert-angular-http-client-update

-> result:

angular-6-http-client-get-post-put-delete-request-to-nodejs-restapi-press-update-button

– Delete ‘Peter’ customer:

angular-6-http-client-get-post-put-delete-request-nodejs-restapi-angular-http-client-delete

– Add a new customer:

angular-6-http-client-get-post-put-delete-request-nodejs-restapi-angular-http-client-post-a-customer

-> result:

angular-6-http-client-get-post-put-delete-request-nodejs-restapi-angular-http-post-a-customer

– Check final customer’s list:

angular-6-http-client-get-post-put-delete-request-nodejs-restapi-final-check

Node.js RestAPIs

Node.js exposes 5 RestAPIs as below:

  • router.post(‘/api/customers’, customers.create);
  • router.get(‘/api/customers’, customers.findAll);
  • router.get(‘/api/customers/:id’, customers.findOne);
  • router.put(‘/api/customers’, customers.update);
  • router.delete(‘/api/customers/:id’, customers.delete);

– Configure cross-origin for Angular-Client which running at port: 4200.


const cors = require('cors')
const corsOptions = {
  origin: 'http://localhost:4200',
  optionsSuccessStatus: 200
}
app.use(cors(corsOptions))

Angular 6 HttpClient

Use Angular HttpClient APIs to do Get/Post/Put/Delete requests to Node.js RestAPIs:


// 1. GET All Customers from remote SpringBoot API @GetMapping(value="/api/customers")
getCustomers (): Observable {
	return this.http.get(this.customersUrl)
}
 
// 2. GET a Customer from remote SpringBoot API @GetMapping(value="/api/customers/{id}")
getCustomer(id: number): Observable {
	const url = `${this.customersUrl}/${id}`;
	return this.http.get(url);
}
 
// 3. POST a Customer to remote SpringBoot API @PostMapping(value="/api/customers")
addCustomer (customer: Customer): Observable {
	return this.http.post(this.customersUrl, customer, httpOptions);
}
 
// 4.DELETE a Customer from remote SpringBoot API @DeleteMapping(value="/api/customers/{id}")
deleteCustomer (customer: Customer | number): Observable {
	const id = typeof customer === 'number' ? customer : customer.id;
	const url = `${this.customersUrl}/${id}`;
 
	return this.http.delete(url, httpOptions);
}
 
// 5. PUT a Customer to remote SpringBoot API @PutMapping(value="/api/customers")
updateCustomer (customer: Customer): Observable {
	return this.http.put(this.customersUrl, customer, httpOptions);
}

Practice

Node.js RestAPIs

Setup Node.js/Express project

Create application directory:


mkdir nodejs-restapi
cd nodejs-restapi

Use the npm init to create ‘package.json’ file:


nodejs-restapi>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install ` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (nodejs-restapi)
version: (1.0.0)
description: Node.js RestAPI - GET/POST/PUT/DELETE
entry point: (index.js) server.js
test command:
git repository:
keywords: Node.js,RestAPI,
author: grokonez.com
license: (ISC)
About to write to C:\workspace\nodejs-restapi\package.json:

{
  "name": "nodejs-restapi",
  "version": "1.0.0",
  "description": "Node.js RestAPI - GET/POST/PUT/DELETE",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "Node.js",
    "RestAPI"
  ],
  "author": "grokonez.com",
  "license": "ISC"
}


Is this OK? (yes) yes

-> Check content of ‘package.json’ file:


{
  "name": "nodejs-restapi",
  "version": "1.0.0",
  "description": "Node.js RestAPI - GET/POST/PUT/DELETE",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "Node.js",
    "RestAPI"
  ],
  "author": "grokonez.com",
  "license": "ISC"
}

We need express, body-parse and cors modules.
– Express is one of the most popular web frameworks for NodeJs which is built on top of Node.js http module, and adds support for routing, middleware, view system etc.
– Body-parser: parses/extract the body of an incoming HTTP request.
- Cors: a mechanism that uses HTTP headers to tell a browser to let a web application running at one origin (domain) have permission to access selected resources from a server at a different origin.

-> Installing them as commandline npm install express body-parser cors--save :

angular-6-http-client-node.js-rest-apis-post-get-put-delete + install-expres-body-parse-cors-package

-> see ‘package.json’ file:


{
  "name": "nodejs-restapi",
  "version": "1.0.0",
  "description": "Node.js RestAPI - GET/POST/PUT/DELETE",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "Node.js",
    "RestAPI"
  ],
  "author": "grokonez.com",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.18.3",
    "cors": "^2.8.4",
    "express": "^4.16.3"
  }
}

Implement Node.js/Express RestAPIs

- In root folder 'nodejs-restapi', create a ‘server.js’ file:


const express = require('express');
const app = express();
const bodyParser = require('body-parser');
app.use(bodyParser.json())

const cors = require('cors')
const corsOptions = {
  origin: 'http://localhost:4200',
  optionsSuccessStatus: 200
}
app.use(cors(corsOptions))

let customerRouter = require('./app/routes/customer.routes.js');
app.use('/', customerRouter);

// Create a Server
let server = app.listen(8080, function () {
 
  let host = server.address().address
  let port = server.address().port
 
  console.log("App listening at http://%s:%s", host, port)
})

Implement Express Application ->

In root folder ‘nodejs-restapi’, create a router folder ‘/app/routes’. Then create a file ‘/app/routes/customer.routes.js’ for routing requests ‘GET/POST/DELETE/UPDATE’:


let express = require('express');
let router = express.Router();

let customers = require('../controllers/customer.controller.js');

// Create a new Customer
router.post('/api/customers', customers.create);

// Retrieve all Customer
router.get('/api/customers', customers.findAll);

// Retrieve a single Customer by Id
router.get('/api/customers/:id', customers.findOne);

// Update a Customer with Id
router.put('/api/customers', customers.update);

// Delete a Customer with Id
router.delete('/api/customers/:id', customers.delete);

module.exports = router;

In root folder ‘nodejs-restapi’, create a controller folder ‘/app/controllers’. Then create a file ‘/app/controllers/customer.controller.js’ that contains methods for executing above URL requests:


let customers = {
				customer1: {
					id: 1,
					firstname: "Joe",
					lastname: "Thomas",
					age: 36
				},
				customer2: {
					id: 2,
					firstname: "Peter",
					lastname: "Smith",
					age: 18
				},
				customer3: {
					id: 3,
					firstname: "Lauren",
					lastname: "Taylor",
					age: 31
				},
				customer4: {
					id: 4,
					firstname: "Mary",
					lastname: "Taylor",
					age: 24
				},
				customer5: {
					id: 5,
					firstname: "David",
					lastname: "Moore",
					age: 25
				},
				customer6: {
					id: 6,
					firstname: "Holly",
					lastname: "Davies",
					age: 27
				},
				customer7: {
					id: 7,
					firstname: "Michael",
					lastname: "Brown",
					age: 45
				}
			}
 
exports.create = function(req, res) {
	// find the largest ID
	let arr = Object.keys( customers ).map(function ( key ) { return customers[key].id; });
	let newId = Math.max.apply( null, arr ) + 1;
	
	let newCustomer = req.body;
	newCustomer.id = newId;
    customers["customer" + newId] = newCustomer;
    res.json(newCustomer);
};
 
exports.findAll = function(req, res) {
    res.json(Object.values(customers));  
};
 
exports.findOne = function(req, res) {
    let customer = customers["customer" + req.params.id];
    res.json(customer);
};
 
exports.update = function(req, res) {
	let updatedCustomer = req.body; 
	customers["customer" + updatedCustomer.id] = updatedCustomer;
	res.json({msg: "Customer Updated Successfully!"});
};
 
exports.delete = function(req, res) {
    delete customers["customer" + req.params.id];
    res.json({msg: "Customer Deleted Successfully!"});
};

Angular 6 Client

– Create Angular project:


ng new angular6-httpclient

– Generate:

  • Customer Class
  • Customer Service
  • Customer Components
  • App Routing Module

-> Details:


ng generate class Customer
ng generate service Customer
ng generate component Customer
ng generate component CustomerDetails
ng generate component AddCustomer
ng generate module AppRouting

– Install Bootstrap 4:


npm install bootstrap jquery --save

-> Configure installed Bootstrap & JQuery in angular.json file:


...
 
"styles": [
  "src/styles.css",
  "node_modules/bootstrap/dist/css/bootstrap.min.css"
],
"scripts": [
  "node_modules/jquery/dist/jquery.min.js",
  "node_modules/bootstrap/dist/js/bootstrap.min.js"
]
 
...

Data Model

Implement Customer model customer.ts :


export class Customer {
    id: number;
    firstname: string;
    lastname: string;
    age: number;
}

Configure AppModule

In the developed application, we use:

  • Angular Forms -> for building form
  • HttpClient -> for http Get/Post/Put/Delete requests
  • AppRouting -> for app routing

-> Modify 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';
 
import { AppRoutingModule }     from './app-routing/app-routing.module';
 
import { AppComponent } from './app.component';
import { CustomerComponent } from './customer/customer.component';
import { CustomerDetailsComponent } from './customer-details/customer-details.component';
import { AddCustomerComponent } from './add-customer/add-customer.component';
 
@NgModule({
  declarations: [
    AppComponent,
    CustomerComponent,
    CustomerDetailsComponent,
    AddCustomerComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

HttpClient DataService

Implement CustomerService customer.service.ts with HttpClient for Get/Post/Put/Delete:

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Customer } from './customer';

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

@Injectable({
  providedIn: 'root'
})
export class CustomerService {
  private customersUrl = 'http://localhost:8080/api/customers';  // URL to web api
  constructor( 
    private http: HttpClient
  ) { }

  getCustomers (): Observable {
    return this.http.get(this.customersUrl)
  }

  getCustomer(id: number): Observable {
    const url = `${this.customersUrl}/${id}`;
    return this.http.get(url);
  }

  addCustomer (customer: Customer): Observable {
    return this.http.post(this.customersUrl, customer, httpOptions);
  }

  deleteCustomer (customer: Customer | number): Observable {
    const id = typeof customer === 'number' ? customer : customer.id;
    const url = `${this.customersUrl}/${id}`;

    return this.http.delete(url, httpOptions);
  }

  updateCustomer (customer: Customer): Observable {
    return this.http.put(this.customersUrl, customer, httpOptions);
  }
}

Angular Router

Implement App-Routing module app-routing.module.ts:


import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { CustomerComponent } from '../customer/customer.component';
import { AddCustomerComponent } from '../add-customer/add-customer.component';
import { CustomerDetailsComponent } from '../customer-details/customer-details.component';
 
const routes: Routes = [
   { 
     path: 'customers', 
     component: CustomerComponent 
   },
   { 
     path: 'customer/add', 
     component: AddCustomerComponent 
   },
   { 
     path: 'customers/:id', 
     component: CustomerDetailsComponent 
   },
   { 
     path: '', 
     redirectTo: 'customers', 
     pathMatch: 'full'
   }, 
];
 
@NgModule({
  imports: [ RouterModule.forRoot(routes) ],
  exports: [ RouterModule ]
})
 
export class AppRoutingModule {}

Router Outlet & Router Links

-> Questions:

  • How to show Componenets with Angular Routers? -> Solution: using Router Outlet
  • How to handle the routing that comes from user’s actions? (like clicks on anchor tag) -> Solution: using Router Link

-> We can achieve above functions by using Angular’s router-outlet and routerLink.

Modify the template file app.component.html of AppComponenet component as below:

<div class="container">
  <div class="row">
    <div class="col-sm-4">  
      <h1>Angular HttpClient</h1>
      <ul class="nav justify-content-center">
          <li class="nav-item">
              <a routerLink="customers" class="btn btn-light btn-sm" role="button" routerLinkActive="active">Retrieve</a> 
          </li>
          <li class="nav-item">
              <a routerLink="customer/add" class="btn btn-light btn-sm" role="button" routerLinkActive="active">Create</a>
          </li>
      </ul>
      <hr>
      <router-outlet></router-outlet>
    </div>
  </div>
</div>

Customer Component

customer Component ->

angular-6-http-client-get-post-put-delete-request-nodejs-restapi-retrieve-all-customer-angular-http-client-get-request-1

– Implement CustomerComponent class customer.component.ts:


import { Component, OnInit } from '@angular/core';
import { Customer } from '../customer';
import { CustomerService } from '../customer.service';
 
 
@Component({
  selector: 'app-customer',
  templateUrl: './customer.component.html',
  styleUrls: ['./customer.component.css']
})
 
export class CustomerComponent  implements OnInit {
 
  customers: Customer[];
 
  constructor(private customerService: CustomerService) {}
 
  ngOnInit(): void {
     this.getCustomers();
  }
 
  getCustomers() {
    return this.customerService.getCustomers()
               .subscribe(
                 customers => {
                  console.log(customers);
                  this.customers = customers
                 }
                );
 }
}

– Implement the template customer.component.html :

<h5>All Customers</h5>
<div *ngFor="let cust of customers">
  <a [routerLink]="['/customers', cust.id]" style="color:black"><span class="badge badge-dark">{{cust.id}}</span> -> {{ cust.firstname }}</a>
</div>

Customer Detail Component

Customer Detail ->

angular-6-http-client-get-post-put-delete-request-nodejs-restapi-customer-details

-> results:

angular-6-http-client-get-post-put-delete-request-nodejs-restapi-press-update-button-1

angular-6-http-client-get-post-put-delete-request-nodejs-restapi-angular-http-client-delete-1

– Implement CustomerDetails class customer-details.component.ts:


import { Component, OnInit } from '@angular/core';
import { Customer } from '../customer';
import { CustomerService } from '../customer.service';
 
import { ActivatedRoute, Params } from '@angular/router';
import { Location } from '@angular/common';
 
@Component({
  selector: 'app-customer-details',
  templateUrl: './customer-details.component.html',
  styleUrls: ['./customer-details.component.css']
})
export class CustomerDetailsComponent implements OnInit {
 
  customer = new Customer() ;
  submitted = false;
  message: string;
 
  constructor(
    private customerService: CustomerService,
    private route: ActivatedRoute,
    private location: Location
  ) {}
 
  ngOnInit(): void {
    const id = +this.route.snapshot.paramMap.get('id');
    this.customerService.getCustomer(id)
      .subscribe(customer => this.customer = customer);
  }
 
  update(): void {
    this.submitted = true;
    this.customerService.updateCustomer(this.customer)
        .subscribe(() => this.message = "Customer Updated Successfully!");
  }
 
  delete(): void {
    this.submitted = true;
    this.customerService.deleteCustomer(this.customer.id)
        .subscribe(()=> this.message = "Customer Deleted Successfully!");
  }
 
  goBack(): void {
    this.location.back();
  }
}

– Implement CustomerDetailsComponent template customer-details.component.html :

<h4><span class="badge badge-light ">{{customer.id}}</span> -> {{customer.firstname}}</h4>
<div [hidden]="submitted">
    <form (ngSubmit)="update()" #detailCustomerForm="ngForm">
      <div class="form-group">
        <label for="firstname">First Name</label>
        <input type="text" class="form-control" id="firstname" required
        [(ngModel)]="customer.firstname" name="firstname" #firstname="ngModel">
        <div [hidden]="firstname.valid || firstname.pristine"
             class="alert alert-danger">
            First Name is required
        </div>
      </div>
      
      <div class="form-group">
        <label for="lastname">Last Name</label>
        <input type="text" class="form-control" id="lastname" required
        [(ngModel)]="customer.lastname" name="lastname" #lastname="ngModel">
        <div [hidden]="lastname.valid || lastname.pristine"
             class="alert alert-danger">
            Last Name is required
        </div>
      </div>
      
      <div class="form-group">
        <label for="age">Age</label>
        <input type="number" class="form-control" id="age" required
        [(ngModel)]="customer.age" name="age" #age="ngModel">
        <div [hidden]="age.valid || age.pristine"
             class="alert alert-danger">
            Age is required
        </div>
      </div>

      <div class="btn-group btn-group-sm">
        <button type="button" class="btn btn-dark" (click)="goBack()">Back</button>
        <button type="submit" class="btn btn-dark" (click)="update()" [disabled]="!detailCustomerForm.form.valid">Update</button>
        <button type="button" class="btn btn-dark" (click)="delete()">Delete</button>
	    </div>
    </form>
</div>
 
<div [hidden]="!submitted">
    <p>{{message}}</p>
    <div class="btn-group btn-group-sm">
      <button type="button" class="btn btn-dark" (click)="goBack()">Back</button>
    </div>
</div>

We can change the value of ng-valid & ng-invalid for more visual feedback,
-> Create ./assets/forms.css file:


.ng-valid[required], .ng-valid.required  {
    border-left: 5px solid rgba(32, 77, 32, 0.623);
}
 
.ng-invalid:not(form)  {
    border-left: 5px solid rgb(148, 27, 27);
}

Add ./assets/forms.css file to index.html :

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Angular6Httpclient</title>
  <base href="/">
 
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link rel="stylesheet" href="assets/forms.css">
</head>
<body>
  <app-root></app-root>
</body>
</html>

Add-Customer Component

AddCustomer Component ->

angular-6-http-client-get-post-put-delete-request-nodejs-restapi-angular-http-client-post-a-customer-1

-> result:

angular-6-http-client-get-post-put-delete-request-nodejs-restapi-angular-http-post-a-customer-1

– Implement AddCustomerComponent class add-customer.component.ts:


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

import { Location } from '@angular/common';

@Component({
  selector: 'app-add-customer',
  templateUrl: './add-customer.component.html',
  styleUrls: ['./add-customer.component.css']
})

export class AddCustomerComponent{

  customer = new Customer();
  submitted = false;

  constructor(
    private customerService: CustomerService,
    private location: Location
  ) { }

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

 addCustomer() {
   this.submitted = true;
   this.save();
 }

  goBack(): void {
    this.location.back();
  }

  private save(): void {
    this.customerService.addCustomer(this.customer)
        .subscribe();
  }
}

– Implement the template add-customer.component.html:

<h3>Add Customer</h3>
<div [hidden]="submitted">
    <form #addCustomerForm="ngForm">

      <div class="form-group">
        <label for="firstname">First Name</label>
        <input type="text" class="form-control" id="firstname" placeholder="Give Customer's FirstName" 
        required
        [(ngModel)]="customer.firstname" name="firstname" #firstname="ngModel">
        <div [hidden]="firstname.valid || firstname.pristine"
             class="alert alert-danger">
            First Name is required
        </div>
      </div>
      
      <div class="form-group">
        <label for="lastname">Last Name</label>
        <input type="text" class="form-control" id="lastname" placeholder="Give Customer's LastName"
        required
        [(ngModel)]="customer.lastname" name="lastname" #lastname="ngModel">
        <div [hidden]="lastname.valid || lastname.pristine"
             class="alert alert-danger">
            Last Name is required
        </div>
      </div>
      
      <div class="form-group">
        <label for="age">Age</label>
        <input type="number" class="form-control" id="age" 
        placeholder="Give Customer's Age"
        required
        [(ngModel)]="customer.age" name="age" #age="ngModel">
        <div [hidden]="age.valid || age.pristine"
             class="alert alert-danger">
            Age is required
        </div>
      </div>
      
      <div class="btn-group btn-group-sm">
      	<button type="button" class="btn btn-dark" (click)="goBack()">Back</button>
      	<button type="button" class="btn btn-dark" (click)="addCustomer()" [disabled]="!addCustomerForm.form.valid">Add</button>
      </div>
    </form>
</div>

<div [hidden]="!submitted">
  <p>Submitted Successfully! -> <span class="badge badge-light">{{customer.firstname}}  {{customer.lastname}}</span></p>
	<div class="btn-group btn-group-sm">
    	<button type="button" class="btn btn-dark" (click)="goBack()">Back</button>
    	<button type="button" class="btn btn-dark" (click)="newCustomer(); addCustomerForm.reset()">Continue</button>
    </div>
</div>

SourceCode

- Angular-6-Http-Client
- Node.js-RestAPIs



By grokonez | July 1, 2018.

Last updated on March 28, 2021.



Related Posts


3 thoughts on “Node.js/Express RestAPIs – Angular 6 HttpClient – Get/Post/Put/Delete requests + Bootstrap 4”

  1. build the angular project into a folder that you app.get into your node.js server, so that when you run the node server, the built angular app is opened, then just upload that to anywhere heroku etc

Got Something To Say:

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

*