Angular - HTTP PUT Request



The HTTP PUT

The HTTP standard verb PUT can be used in the HTTP protocol to request the creation or update of a resource (data) on the server. The purpose of the PUT method is to send data to the server to create or update the resource based on the provided data.

The server processes the data and either "creates or updates" the requested resource. Once the data is created or updated, the server returns a response to the client.

In Angular, theHttpClientservice class provides aput()method to send data to the server using the HTTP PUT verb. Let us learn more about this method, including its signature, options, and real-time usage.

Signature of the put() Method

The signature (different from syntax) of the HttpClient put() method is as follows −

put<T>(url: string, body: any, options?: Object): Observable<T>

Here,

  • URL − The URL to which the PUT request is sent.
  • body − It represents the data to be sent (updated) to the server. Normally, it will be in JSON format.
  • options − It represents the options to be sent along with the request, such as headers, query parameters, etc.
  • Observable<T> − The return type, where 'T' represents the expected response type.

Options

Here are the available options for the HttpClient method −

observe

Observe is used to specify which part of the response has to be observed during the server communication. Based on the observe option, either the full or part of the response will be returned as Observable. The possible values are body, events, and response.

body: Retrieves only the body content of the response from the HTTP request as Observable<R>, where R is based on the responseType option and the requested type (e.g., Expense) of data.

this.http.put<Expense>(<url>, { 
'observe': 'body', 
'responseType' : 'json'
})

Here,

  • JSON is the format used to interpret the response body.
  • Expense is the requested type used to format the response body and returns as Observable<Expense>.

events: Retrieves the events fired in a response stream along with the corresponding response body as Observable<HttpEvent><R>>, where R is based on the responseType option and the requested type (e.g., Expense) of data.

this.http.put<Expense>(<url>, 
{ 'observe': 'events', 
'responseType' : 'json' 
})

Here,

  • JSON is the format used to interpret the response body.
  • Expense is the requested type used to format the response body and returns Observable<HttpEvent<Expense>>.

response: Retrieves the complete response from the HTTP request as Observable<HttpResponse<R>>, where R is based on the responseType option (which we will check in the next section) and the requested type (e.g., Expense) of data.

this.http.put<Expense>(<url>, { 
'observe': 'response', 
'responseType' : 'json' 
})

Here,

  • JSON is the format used to interprets the response body.
  • The Expense is the requested type used to format the response body and returns Observable<HttpResponse<Expense>>.

responseType

TheresponseTypeinterprets the response body. It can have four possible values as shown below −

  • arraybuffer
  • blob
  • text
  • json

Let us understand these four options one by one:

arraybuffer: Interprets the response body as a generic raw binary data buffer and returns Observable. It can be used to stream audio/video content −

this.http.put(<url>, { 
'observe': 'body', 
'responseType' : 'arraybuffer' 
})

blob: Interprets the response body as the binary format and returns Observable<blob>. It can be used to download large files −

this.http.put(<url>, {
'observe': 'body',
'responseType' : 'blob' 
})

text: Interprets the response body as plain text format and returns Observable<String>. It can be used to represent text-based data −

this.http.put(<url>, {
'observe': 'body',
'responseType' : 'json' 
})

JSON: Interprets the response body in JSON format and returns Observable<R>, where R is the requested type (e.g., Expense) of data. It can be further encoded into any type by specifying the type variable (R) in the method as shown below −

this.http.put<Expense>(<url>, {
'observe': 'body',
'responseType' : 'json' }
)

Based on the observe and responseType, HttpClient will return Observable with a different type variable. Let us check a few combinations of observation and responseType to better understand the concept:

  • observe => body and responseType => JSON

    Returns the Observable. R represents the type variable.

  • observe => response and responseType => JSON

    Returns the Observable<HttpResponse>. R represents the type variable and encodes the response body.

  • observe => events and responseType => JSON

    Returns the Observable<HttpEvent>. The response body is encoded as ArrayBuffer.

  • observe => events and responseType => arraybuffer

    Returns the Observable<HttpEvent>. Responses body is encoded as ArrayBuffer.

  • observe => response and responseType => blob

    Returns the Observable<HttpEvent>. The response body is encoded as ArrayBuffer.

  • observe => response and responseType => text

    Returns the Observable<HttpResponse>. The response body is encoded as ArrayBuffer.

  • We can combine observe and responseType to create many more combinations as necessary.

headers

Theheaders are specified as the HTTP headers. It can include a standard HTTP header as a key/value pair or can encode the data in the HttpHeaders class. A sample header as a key/value pair is as follows:

{ 'Content-type': 'application/json' }

It specifies that the request content type is JSON. We can also use the HttpHeaders class provided by angular to create HTTP headers. A sample set of header information using HttpHeaders is as follows −

// create header using `HttpHeaders`
const headers = new HttpHeaders()
   .set('content-type', 'application/json')
   .set('Access-Control-Allow-Origin', '*');

this.http.put<Expense>(<url>, { 
   'observe': 'body', 
   'responseType' : 'json', 
   'headers': headers 
})

params

Theparamsrepresent the serialized request parameter in application/x-www-form-urlencoded format. It can include params as a key/value pair or can encode the data in the HttpParams class. A sample parameter as a key/value pair is as follows −

{ 'name': 'john' }

It specifies that the request param key is a name, and its value is "john". We can also use the HttpParams class provided by angular to create parameters. A sample set of parameters using HttpParams is as follows −

// create parameters using `HttpParams`
const params = new HttpParams()
   .set('name', 'john')
   .set('age', 25)
   .set('active', true;

this.http.put<Expense>(<url>, { 
   'observe': 'body', 
   'responseType' : 'json', 
   params: params 
})

context

Thecontextsends arbitrary values as key/value pairs with type safety and without key conflict. It is used as a source of information for interceptors acting as middle-ware between client and server. Angular provides a special class, HttpContext to encode the context information. A sample context is as follows −

// create a key using HttpContextToken
export const IS_AUTH_ENABLED = new HttpContextToken<boolean>(() => false);

// set data for the context
let authContext = new HttpContext().set(IS_AUTH_ENABLED, true)

this.http.request<Expense>('GET', <url>, { 
   'observe': 'body', 
   'responseType' : 'json', 
   context: authContext 
})

Here,

  • HttpContextToken is used to create the key along the value type.
  • IS_AUTH_ENABLED is the key, and its type is boolean.

reportProgress

The reportProgressspecifies whether to send back the progress of the request (communication) from the server. It can be used to show the progress of large file uploads through web API.

this.http.put<Expense>(<url>, { 
   'observe': 'events', 
   'responseType' : 'json', 
   reportProgress: true 
})

withCredentials

The withCredentials is used to specify whether the request should be sent with outgoing credentials (cookies). It accepts the boolean value.

this.http.put<Expense>(<url>, { 
   'observe': 'body', 
   'responseType' : 'json', 
   withCredentials: true 
})

transferCache

ThetransferCachespecifies whether the request should be cached. It accepts the boolean value or HttpTransferCacheOptions value. HttpTransferCacheOptions encode dynamic logic to filter requests to be cached based on a custom filter function and override default cache behavior.

this.http.put<Expense>(<url>, { 
   'observe': 'body', 
   'responseType' : 'json', 
   transferCache: true 
})

Working example

To work out the HTTP client-server communication, we need to set up a web application and need to exposes a set of web API. The web API can be requested from the client. Let us create a sample server application, Expense API App, and provide CRUD REST API (mainly PUT requests) for expenses.

Step 1: Go to your favorite workspace as shown below −

cd /go/to/your/favorite/workspace

Step 2: Create a new folder with the name expense-rest-apiand move into the folder −

mkdir expense-rest-api && cd expense-rest-api

Step 3: Create a new application using init sub command provided by npm command as shown below −

npm init

Once you run the above command, it will ask a few questions and answer all of them with default answers.

Step 4: Install express and cors packages to create node based web application −

npm install express cors --save

Step 5: Install express and cors packages to create node-based web applications −

npm install sqlite3 --save

Step 6: Create a new file with the name sqlitedb.js, and place the below code to initialize the database with the expense table and sample expense entries. An expense table will be used to store the expense items −

var sqlite3 = require('sqlite3').verbose()
const DBSOURCE = "expensedb.sqlite"

let db = new sqlite3.Database(DBSOURCE, (err) => {
   if (err) {
      console.error(err.message)
      throw err
   }else{
      console.log('Connected to the SQLite database.')
      db.run(`CREATE TABLE IF NOT EXISTS expense (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            item text, 
            amount real, 
            category text, 
            location text, 
            spendOn text, 
            createdOn text 
         )`,
         (err) => {
            if (err) {
               console.log(err);
            }else{
               var insert = 'INSERT INTO expense (item, amount, category, location, spendOn, createdOn) VALUES (?,?,?,?,?,?)'
   
               db.run(insert, ['Pizza', 10, 'Food', 'KFC', '2020-05-26 10:10', '2020-05-26 10:10'])
               db.run(insert, ['Pizza', 9, 'Food', 'Mcdonald', '2020-05-28 11:10', '2020-05-28 11:10'])
               db.run(insert, ['Pizza', 12, 'Food', 'Mcdonald', '2020-05-29 09:22', '2020-05-29 09:22'])
               db.run(insert, ['Pizza', 15, 'Food', 'KFC', '2020-06-06 16:18', '2020-06-06 16:18'])
               db.run(insert, ['Pizza', 14, 'Food', 'Mcdonald', '2020-06-01 18:14', '2020-05-01 18:14'])
            }
         }
      );  
   }
});

module.exports = db

Step 7: Now, open theindex.js(if not found, create it manually) and place the below code −

var express = require("express")
var cors = require('cors')
var db = require("./sqlitedb.js")

var app = express()
app.use(cors());

var bodyParser = require("body-parser");
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

var HTTP_PORT = 8000
app.listen(HTTP_PORT, () => {
   console.log("Server running on port %PORT%".replace("%PORT%", HTTP_PORT))
});

app.get("/", (req, res, next) => {
   res.json({ "message": "Ok" })
});

app.get("/api/expense", (req, res, next) => {
   var sql = "select * from expense"
   var params = []
   db.all(sql, params, (err, rows) => {
      if (err) {
         res.status(400).json({ "error": err.message });
         return;
      }
      res.json(rows)
   });

});

app.get("/api/expense/:id", (req, res, next) => {
   var sql = "select * from expense where id = ?"
   var params = [req.params.id]
   db.get(sql, params, (err, row) => {
      if (err) {
         res.status(400).json({ "error": err.message });
         return;
      }
   res.json(row)
   });
});

app.put("/api/expense/:id", (req, res, next) => {
   if (req.params.id == null) {
      res.status(400).json({ "error": "Resource (Expense) Id is not send." })
      return
   }
   
   var data = {
      id: req.params.id,
      item: req.body.item,
      amount: req.body.amount,
      category: req.body.category,
      location: req.body.location,
      spendOn: req.body.spendOn
   }
   
   var sql = 'SELECT count(*) AS cnt FROM expense WHERE id = ?'
   var params = [data.id]
   db.get(sql, params, function (err, result) {
   if (err) {
      res.status(400).json({ "error": err.message })
      return;
   }
   
   if (result.cnt == 0) {
      var sql = 'INSERT INTO expense (id, item, amount, category, location, spendOn, createdOn) VALUES (?, ?,?,?,?,?,?)'
      var params = [data.id, data.item, data.amount, data.category, data.location, data.spendOn, data.createdOn]
      db.run(sql, params, function (err, result) {
         if (err) {
            res.status(400).json({ "error": err.message })
            return;
         }
         console.log(result)
         res.json(data);
      });
   } else {
      db.run(
         `UPDATE expense SET
            item = ?, 
            
            amount = ?,
            category = ?, 
            location = ?, 
            
            spendOn = ? 
            WHERE id = ?`,
         [data.item, data.amount, data.category, data.location, data.spendOn, data.id],
         function (err, result) {
            if (err) {
               console.log(err);
               res.status(400).json({ "error": res.message })
               return;
            }
            res.json(data)
         });
      }
   });

})

app.use(function (req, res) {
   res.status(404);
});

Here, the code will create six REST API endpoints below-mentioned REST API endpoints −

  • /endpointreturns an "OK" message to make sure the application is working fine.
  • /api/expenseendpoint returns all expense items available in the database.
  • /api/expense/:idendpoint returns the expense entry based on the expense entry id.
  • /api/expense/:idendpoint with put verb will update the expense entry based on the expense entry id.

Step 8: Run the application using the below command −

node index.js

Step 9: To test the application open your friendly browser (chrome) and go to http://localhost:8000/. It should return the below message if the application is working fine −

{ 
   "message": "Ok" 
}

Angular Sample Application

Let's create a working angular application to put a resource into the above server application and then get all expense items from the server including the new resource by using the HttpClient service class.

Step 1: Create a new angular application by running ng new command as shown below −

ng new my-http-app

Enable angular routing and CSS as shown below −

? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? CSS

Step 2: Enable http communication in the application by importing HttpClientModule in the component configuration file (app.component.ts) −

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [CommonModule, RouterOutlet, HttpClientModule],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent {
  title = 'my-http-app';
}

Here,

  • Imported the HttpClientModule from @angular/common/http module.
  • Added the HttpClientModule into the imports section of the @NgModule configuration.

Step 3: Create a new interface, Expense to represent our expense item −

interface Expense {
   id?: Number,
   item: String,
   amount: Number,
   category: String,
   location: String,
   spendOn: Date
}

export default Expense;

Here,

  • An id is set as an optional property.

Step 4: Create a new component, ListExpenses to show the expense items from the server −

ng generate component ListExpenses

It will create the component as shown below −

CREATE src/app/list-expenses/list-expenses.component.css (0 bytes)
CREATE src/app/list-expenses/list-expenses.component.html (28 bytes)
CREATE src/app/list-expenses/list-expenses.component.spec.ts (602 bytes)
CREATE src/app/list-expenses/list-expenses.component.ts (229 bytes)
UPDATE src/app/app.module.ts (581 bytes)

Step 5: Include our new component into the App root component view, app.component.html as shown below −

<app-list-expenses></app-list-expenses>
<router-outlet></router-outlet>

Step 6:Inject the HttpClient into the ListExpenses component through the constructor as shown below −

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
   selector: 'app-list-expenses',
   templateUrl: './list-expenses.component.html',
   styleUrls: ['./list-expenses.component.css']
})
export class ListExpensesComponent {

   constructor(private http: HttpClient) { }
}

Step 7: Implement the OnInit life cycle hook to request the server for expenses after the initialization of the ListExpenses component −

export class ListExpensesComponent implements OnInit{
   constructor(private http: HttpClient) { }
   ngOnInit(): void {
   
   }
}

Step 8: Create a local variable, expenses to hold our expenses from the server −

export class ListExpensesComponent implements OnInit{
   expenses: Expense[] = [];   
   constructor(private http: HttpClient) { }   
   ngOnInit(): void {
   
   }
}

Step 9: Create a local variable, a newexpense to hold the new expense created in the server −

export class ListExpensesComponent implements OnInit{
   expenses: Expense[] = [];
   newexpense: Expense | null = null;   
   constructor(private http: HttpClient) { }   
   ngOnInit(): void {
   
   }
}

Step 10: Set the new expense item with sample data as shown below −

export class ListExpensesComponent implements OnInit {
   expenses: Expense[] = [];
   newexpense: Expense | null = null;   
   constructor(private http: HttpClient) { }   
   ngOnInit(): void {   
      var spend_date = new Date();
      spend_date.setDate(spend_date.getDate() - 1);
      this.newexpense = {
         'item': 'new item ' + Math.floor(Math.random() * 10),
         'amount': Math.floor(Math.random() * 100),
         'category': 'Food',
         'location': 'KFC',
         'spendOn': spend_date
      }   
   }
}

Here,

  • Used random method to set item name and amount.
  • set spend date as yesterday.

Step 11: Call the put() method of this.http (HttpClient instance) object by passing the put url & our new expense item and get the updated expense object from the server −

export class ListExpensesComponent implements OnInit {
   expenses: Expense[] = [];
   newexpense: Expense | null = null;   
   constructor(private http: HttpClient) { }   
   ngOnInit(): void {   
      var spend_date = new Date();
      spend_date.setDate(spend_date.getDate() - 1);
      this.newexpense = {
         'item': 'new item ' + Math.floor(Math.random() * 10),
         'amount': Math.floor(Math.random() * 100),
         'category': 'Food',
         'location': 'KFC',
         'spendOn': spend_date
      }
      
      this.http.put<Expense>('http://localhost:8000/api/expense/1', this.newexpense,{
         'observe': 'body',
         'responseType': 'json'
      })
      .subscribe( data => {
         this.newexpense = data as Expense;
         console.log(data)
      });      
   }
}

Step 12: Call the get() method of this.http (HttpClient instance) object by passing the list expenses URL and options and retrieving the expense object from the server. Then, set the expenses into our local variable, expenses −

export class ListExpensesComponent implements OnInit {
   expenses: Expense[] = [];
   newexpense: Expense | null = null;
   constructor(private http: HttpClient) { }
   ngOnInit(): void {
      var spend_date = new Date();
      spend_date.setDate(spend_date.getDate() - 1);
      this.newexpense = {
         'item': 'new item ' + Math.floor(Math.random() * 10),
         'amount': Math.floor(Math.random() * 100),
         'category': 'Food',
         'location': 'KFC',
         'spendOn': spend_date
      }
      this.http.put<Expense>('http://localhost:8000/api/expense/1',
      this.newexpense,{
         'observe': 'body',
         'responseType': 'json'
      })
      .subscribe( data => {
         this.newexpense = data as Expense;
         console.log(data)
      });
      this.http.get<Expense[]>('http://localhost:8000/api/expense',{
         'observe': 'body',
         'responseType': 'json'
      })
      .subscribe( data => {
         this.expenses = data as Expense[]
         console.log(this.expenses)
      })
   }
}

Here,

  • Sets the Expense[] as the type of the object returned by the server. The server will send the array of expense objects along with a new expense object in its body in the JSON format.
  • Subscribed to the request (this.http.get) object. Then parsed the subscribed data as an array of expense objects and set it to a local expense variable (this.expenses).

The complete code of the ListExpensesComponent is as follows −

import { Component, OnInit } from '@angular/core';
import { HttpClient, HttpRequest, HttpResponse, HttpEvent, HttpParams }
from '@angular/common/http';
import Expense from '../Expense';
@Component({
   selector: 'app-list-expenses',
   templateUrl: './list-expenses.component.html',
   styleUrls: ['./list-expenses.component.css']
})
export class ListExpensesComponent implements OnInit {
   expenses: Expense[] = [];
   newexpense: Expense | null = null;
   constructor(private http: HttpClient) { }
   ngOnInit(): void {
      var spend_date = new Date();
      spend_date.setDate(spend_date.getDate() - 1);
      this.newexpense = {
         'item': 'new item ' + Math.floor(Math.random() * 10),
         'amount': Math.floor(Math.random() * 100),
         'category': 'Food',
         'location': 'KFC',
         'spendOn': spend_date
      }
      this.http.put<Expense>('http://localhost:8000/api/expense/1',
      this.newexpense,{
         'observe': 'body',
         'responseType': 'json'
      })
      .subscribe( data => {
         this.newexpense = data as Expense;
         console.log(data)
      });
      this.http.get<Expense[]>('http://localhost:8000/api/expense',{
         'observe': 'body',
         'responseType': 'json'
      })
      .subscribe( data => {
         this.expenses = data as Expense[]
         console.log(this.expenses)
      })
   }
}

Step 14: Next, get the expenses list object and new expense object from the component and render it in our component template page (list-expenses.component.html)

<div>Expense item (id = 1) updated in the server is as follows:</div>
<div>Item: {{newexpense?.item}}</div>
<div>Amount: {{newexpense?.amount}}</div>
<div>Location: {{newexpense?.location}}</div>
<div>Spend On: {{newexpense?.spendOn | date:'short'}}</div>
<div><h3>Expenses</h3></div>
<ul>
   <li *ngFor="let expense of expenses">
      {{expense.item}} @ {{expense.location}} for {{expense.amount}}
      USD on {{expense.spendOn | date:'shortDate' }}
   </li>
</ul>

Here,

  • Expense with id = 1 will get updated in the server.
  • The returned expenses will have the updated expense from the server.

Step 15: Finally, run the application using below command −

ng serve

Step 16: Open the browser and navigate to http://localhost:4200/ URL and check the output−

http_put

Here, the output shows the our expenses as a list of items.

Conclusion

Angular provides an easy way to send data to the server through the HttpClient object.put()is a specific method used to send data to the server. We will learn more HTTP methods to target other HTTP verbs in the upcoming chapters.

Multiple Choice Questions (MCQ's):

Here we have mentioned a few MCQs to test your knowledge on the current concept:

Answer : B

Explanation:

The primary purpose of an HTTP POST request is to send data to a server to create or update a resource.

Answer : B

Explanation:

The body parameter in the HttpClient.put() method is where you define the data that you want to send to the server.

Q 3 − Which option of the HttpClient.put() method is used to specify additional settings like headers?

A − body

B − url

C − options

D − responseType

Answer : C

Explanation:

The options parameter allows you to include additional settings like HTTP headers, query parameters, or other configuration options.

Q 4 − What type of data is sent in the body of the HttpClient.put() request?

A − Binary data

B − JSON data

C − HTML data

D − Plain text data

Answer : B

Explanation:

When using the HttpClient.put() method, the data sent to the server is in JSON format. This is because JSON is a commonly used JSON format for exchanging data between a client (Angular) and a server.

Advertisements