Angular HttpClient put() Example
March 18, 2024
On this page we will learn to execute HTTP PUT request using HttpClient.put()
method in our Angular application. HTTP PUT request is used to create or update resource on server.
Angular
HttpClient
performs HTTP requests. It is an injectable class that has methods to execute HTTP request. These methods are request(), delete(), get(), head(), jsonp(), options(), patch(), post() and put() .
To use
HttpClient.put()
method, we need to pass url, body and configuration options as arguments. It returns an Observable
object. On subscribe to this observable object, HTTP PUT request is performed.
Here we will learn to use
HttpClient.put()
method with examples in detail.
Contents
1. PUT vs POST
1. PUT and POST both contains a body. PUT uses body to update the resource on server and POST uses the body to save resource on the server.2. In POST request, successful response has always a body but in PUT request, successful response may or may not have body.
3. PUT request is idempotent whereas POST request is not idempotent. Being idempotent means calling more than one time successfully, its has the same effect on the server. As we know that PUT is used to update the data on the server. Suppose we are updating a resource against an id and we hit PUT request more than one time successfully, it is equal to one successful hit and there will be no side effect for multiple calling. In POST request, suppose we are generating an order on the server, so on calling multiple POST request, multiple order can be generated for the same request.
4. PUT and POST both requests can read and write data on server, so both are not safe HTTP methods. HTTP methods that perform read-only operations, are considered as safe HTTP methods.
5. The response of PUT is not cacheable whereas the response of POST is cacheable only if fresh information is included.
6. POST method is allowed in HTML forms but PUT method is not allowed in HTML forms.
2. HttpClient.put() Method Structure
HttpClient.put()
method executes HTTP PUT request. put()
creates an observable and that on subscribe, hits HTTP PUT request on server.
put()
method accepts url, body, options as arguments and returns Observable
as following.
put(url: string, body: any, options: { headers?: HttpHeaders; context?: HttpContext; observe?: "body"; params?: HttpParams; reportProgress?: boolean; responseType: "json"; withCredentials?: boolean; }): Observable<T>
url : Endpoint URL.
body : Resource that need to be created or updated.
options : Configures HTTP options. Default value of 'observe' is 'body' and default value of 'responseType' is 'json'.
2. Returns :
put()
method returns an Observable
object and when we subscribe it, PUT request is executed.
3. options :
headers : Configures request headers.
context : Pass arbitrary user defined values.
observe : It has three values. When the value is “response”, complete response is returned. When the value is “body”, only body of response is returned. When the value is “events”, response with events is returned.
params : Configures query parameters.
responseType : The values of response type can be arraybuffer, blob, json and text.
reportProgress : Boolean value to configure if we want the request progress report, particularly when response type is ‘blob’.
withCredentials : Boolean value to configure if authorization header is being sent.
4. overloads :
put()
has following overloads.
1. For observe: 'body' and responseType: 'json', return type is Observable<T>
2. For observe: 'body' and responseType: 'text', return type is Observable<string>
3. For observe: 'body' and responseType: 'arraybuffer', return type is Observable<ArrayBuffer>
4. For observe: 'body' and responseType: 'blob', return type is Observable<Blob>
5. For observe: 'events' and responseType: 'arraybuffer', return type is Observable<HttpEvent<ArrayBuffer>>
6. For observe: 'events' and responseType: 'blob', return type is Observable<HttpEvent<Blob>>
7. For observe: 'events' and responseType: 'text', return type is Observable<HttpEvent<string>>
8. For observe: 'events' and responseType: 'json', return type is Observable<HttpEvent<Object>>
9. For observe: 'events' and responseType: 'json', return type is Observable<HttpEvent<T>>
10. For observe: 'response' and responseType: 'arraybuffer', return type is Observable<HttpResponse<ArrayBuffer>>
11. For observe: 'response' and responseType: 'blob', return type is Observable<HttpResponse<Blob>>
12. For observe: 'response' and responseType: 'text', return type is Observable<HttpResponse<string>>
13. For observe: 'response' and responseType: 'json', return type is Observable<HttpResponse<Object>>
14. For observe: 'response' and responseType: 'json', return type is Observable<HttpResponse<T>>
15. For observe: 'body' and responseType: 'json', return type is Observable<Object>
3. Using HttpClient.put()
1. ConfigureprovideHttpClient()
in standalone application to make HttpClient
available for dependency injection.
export const APP_CONFIG: ApplicationConfig = { providers: [ provideHttpClient(), ------ ] };
HttpClient
.
constructor(private http: HttpClient) { }
HttpClient.put()
needs a URL to hit server, data to send, options for request and response configurations.
updateWriter(writer: Writer): Observable<Writer> { return this.http.put<Writer>("/api/writers", writer); }
To get response as text, configure responseType: 'text' as below.
updateWriter(writer: Writer): Observable<string> { return this.http.put("/api/writers", writer, { responseType: 'text' }); }
4. Pass Headers
1. To configure request herders, we need to configureheaders
property in options argument.
put(url: string, body: any, options: { headers?: HttpHeaders; })
HttpHeaders
creates headers for HTTP request. Its instances are immutable. It provides methods as append()
and set()
to add headers. These methods return a cloned instance of HttpHeaders
with the change.
append()
method appends a new value to the existing set of values.
set()
method sets or modifies a value for a given header. If the header already exists, its value is replaced by new value.
We can also add headers using its constructor while creating its instance.
3. Find the code snippet to configure headers using
HttpHeaders
.
updateBook(book: Book): Observable<Book> { const myHeaders = new HttpHeaders() .append("Content-Type", "application/json") .append("Content-Length", "70"); return this.http.put<Book>("/api/books", book, { headers: myHeaders }); }
headers
property.
return this.http.put<Book>("/api/books", book, { headers: { "Content-Type": "application/json", "Content-Length": "70" } });
5. Pass Query Parameters
1. To pass query parameters to PUT request, useparams
property of options argument of put()
method.
put(url: string, body: any, options: { params?: HttpParams; })
HttpParams
represents query parameters in HTTP request. It is serialized parameters, per the MIME type application/x-www-form-urlencoded. HttpParams
is an immutable class.
We can add parameters to instance of
HttpParams
using its append()
, appendAll
and set()
methods. These methods return the cloned instance of HttpParams
after changing values.
3. Find the code snippet to pass query parameters using
HttpParams
.
updateBook(book: Book): Observable<Book> { const myParams = new HttpParams() .set("bookId", "101") .set("publisher", "XYZ"); return this.http.put<Book>("/api/books", book, { params: myParams }); }
return this.http.put<Book>("/api/books", book, { params: { "bookId": "101", "publisher": "XYZ" } });
6. Pass Context
1. To configure HTTP context, we need to configurecontext
property in options argument.
put(url: string, body: any, options: { context?: HttpContext; })
HttpContext
stores arbitrary user defined values. It provides set()
method to store a value in the context. It replaces the value if already present. We need to pass argument as key/value. Key is a type of HttpContextToken
and value can be any object.
3. Suppose we have a HTTP context token as below.
export const IS_CACHE_ENABLED = new HttpContextToken<boolean>(() => false);
put()
method as below.
updateBook(book: Book): Observable<Book> { const myContext = new HttpContext().set(IS_CACHE_ENABLED, true); return this.http.put<Book>("/api/books", book, { context: myContext }); }
7. Using Credentials
To use credentials with request, we need to enable it input()
request. In options argument, assign true to withCredentials
property as below.
updateBook(book: Book): Observable<Book> { const myHeaders = new HttpHeaders().set('Authorization', `Bearer ${authToken}`); return this.http.put<Book>("/api/books", book, { headers: myHeaders, withCredentials: true }); }
8. Error Handling
In HTTP requests, there can be client-side error or network error. Server application can also throw error such as '500 Internal Server Error', '501 Not Implemented' etc.To handle errors in HTTP requests using
HttpClient
, we need to use Angular HttpErrorResponse
. It represents errors of following type.
1. Non-successful HTTP status,
2. An error while executing the request,
3. Error during parsing the response.
HttpErrorResponse
provides error property using which we can handle errors.
To handle error, I am creating a function that will accept
HttpErrorResponse
as argument.
private hadnleError(error: HttpErrorResponse) { if (error.status === 0) { console.error("Error: A client-side or network error occurred."); } else { console.error("Error: ", error.error); } return throwError(() => new Error("An error occurred.")); }
catchError
as an object of HttpErrorResponse
. We handle the error and then throw the custom error message.
updateBook(book: Book): Observable<Book> { const myHeaders = new HttpHeaders() .append("Content-Type", "application/json") .append("Content-Length", "70"); return this.http.put<Book>("/api/books", book, { headers: myHeaders }).pipe( catchError(this.hadnleError) ); }
We can retry failed request. Sometimes it fixes error automatically because if error is network error then next request could be successful. To retry same request we can use RxJS
retry()
operator.
return this.http.put<Book>("/api/books", book, { headers: myHeaders }).pipe( retry(2), catchError(this.hadnleError) );
retry(2)
will attempt failed request two times. If in the first attempt, request is successful, then second attempt will not be made.
9. Subscribe to putHttpClient.put()
HttpClient.put()
returns Observable
object and when we subscribe this object only then HTTP PUT request is performed.
Here I have a service class and a component. In service class, I am creating methods to update resources on server using
HttpClient
. In component class, I will subscribe the observable object returned by HttpClient.put()
method.
Find the service class containing the methods to execute HTTP PUT request.
import { Injectable } from '@angular/core'; import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http'; import { Observable, catchError, retry, throwError } from 'rxjs'; import { Book } from '../book'; import { Writer } from '../writer'; const authToken = 'AUTH_TOKEN'; @Injectable({ providedIn: 'root' }) export class BookService { constructor(private http: HttpClient) { } updateWriter(writer: Writer): Observable<Writer> { return this.http.put<Writer>("/api/writers", writer); } updateBook(book: Book): Observable<HttpResponse<Book>> { const myHeaders = new HttpHeaders() .append("Content-Type", "application/json") .append("Content-Length", "70") .append('Authorization', `Bearer ${authToken}`); const myParams = new HttpParams() .set("bookId", "101") .set("publisher", "XYZ"); return this.http.put<Book>("/api/books", book, { headers: myHeaders, params: myParams, observe: "response", responseType: "json", withCredentials: true }).pipe( retry(2), catchError(this.hadnleError) ); } private hadnleError(error: HttpErrorResponse) { if (error.status === 0) { console.error("Error: A client-side or network error occurred."); } else { console.error("Error: ", error.error); } return throwError(() => new Error("An error occurred.")); } }
import { Component, OnInit } from '@angular/core'; import { BookService } from './services/book.service'; import { Book } from './book'; import { CommonModule } from '@angular/common'; import { Writer } from './writer'; import { HttpResponse } from '@angular/common/http'; @Component({ selector: 'app-book', standalone: true, imports: [CommonModule], templateUrl: './book.component.html' }) export class BookComponent implements OnInit { constructor(private bookService: BookService) { } ngOnInit() { this.updateWriter(); this.updateBook(); } updateWriter() { const writer: Writer = { id: 21, name: 'Ram', city: 'PrayagRaj' }; this.bookService.updateWriter(writer).subscribe(writer => console.log(writer)); } updateBook() { const book: Book = { id: 111, name: 'Hibernate By Mohan', category: 'Hibernate' }; this.bookService.updateBook(book).subscribe({ next: (response: HttpResponse<Book>) => { console.log(response.body); }, error: (error: any) => { console.error(error); }, complete: () => { console.log("Request completed") } }); } }