Handling Errors in TypeScript

Introduction 

 
In this article, we will see the various errors that occurred while requesting from the client to the server. Also, we will create a sample component and try to console the error object.
 
Prerequisites
  • Basics of TypeScript.
The call to the server doesn't always succeed. Sometimes, it fails.
  1. ngOnInit() {  
  2.     this.service.getPosts()  
  3.     .subscribe(response=> {  
  4.       console.log(response.json());  
  5.       this.posts = response.json();  
  6.     });  
  7.   }  
  8.   
  9. private url = 'http://jsonplaceholder.typicode.com/posts';  
Let’s change the URL to an invalid URL:
  1. private url = 'http://jsonplaceholder404.typicode.com/posts';  
Now run the application. You will see that no items have posted, as you have given an unknown URL. We need to change our implementation to handle errors and give proper error messages to our user.
 
When we try to handle error we will encounter two types of error.
  1. Unexpected Error
  2. Expected Error
There are three types of unexpected error - Server is Offline ( The client is sending a request to the server but the server is not up and responding to the client request), Network is down (when the server is online but the client is not able to reach server ) and unhandled exceptions (server is up and responding and the network is fine, but there are some API related bugs).
 
There are two types of Expected errors that we need to handle: “Not Found” error 404, and “Bad request” error 400.
 
If we have a list of items visible on two different clients. If one client deletes an item (say id:101) then the representation view of the second client will not update. Furthermore, if the second client requests to delete the same item (say id: 101) then the server will respond with a 404 error that the item is not found. This occurs in most cases, that’s why this type of error comes under expected error.
 
The expected error, Bad request is generated when the server responds to the client that the request is bad. Suppose you already registered an email/phone to the server and you try to register the same. Then, the server with their proper check responds with the bad request. This also comes under expected errors.
 
So we have to handle the expected and unexpected errors properly while developing an application.
 
Let's start creating the application.
 
Click here to create the component in which we going to add errors in the service request.
 
Change the URL to as below in postService.ts to check the getPost method.
  1. private url = 'http://jsonplaceholder404.typicode.com/posts';  
Change PostsComponent as below,
 
The subscribe method has an optional parameter named “error”. The type of this parameter is a function that we need to pass that takes an error object and return void. Let’s handle the expected errors.
  1. import { Component, OnInit } from '@angular/core';  
  2. import { PostService } from '../services/post.service';  
  3.   
  4. @Component({  
  5.   selector: 'app-posts',  
  6.   templateUrl: './posts.component.html',  
  7.   styleUrls: ['./posts.component.css']  
  8. })  
  9. export class PostsComponent implements OnInit {    
  10.  posts: any[];  
  11.   constructor(private service: PostService)   
  12.   {      
  13.   }  
  14.   
  15.   ngOnInit() {  
  16.     this.service.getPosts()  
  17.     .subscribe(response=> {  
  18.       this.posts = response.json();  
  19.     }, (error) => {  
  20.       console.log('An unexpected error occured');  
  21.       console.log(error);  
  22.     }, () => {  
  23.       console.log('completed');  
  24.     });  
  25.   }  
  26.   
  27.   createPost(input: HTMLInputElement)  
  28.   {  
  29.     let post = {title: input.value};  
  30.     input.value='';  
  31.     this.service.createPost(post)  
  32.     .subscribe(response => {  
  33.       post['id'] = response.json().id;  
  34.       this.posts.splice(0,0,post);        
  35.     }, (error: Response) => {  
  36.       if(error.status == 400)  
  37.         console.log(error.json());  
  38.       else {  
  39.         console.log('An unexpected error occured');  
  40.         console.log(error);  
  41.       }        
  42.     });  
  43.   }  
  44.   
  45.   updatePost(post)  
  46.   {  
  47.     this.service.updatePost(post)  
  48.     .subscribe(response=> {  
  49.       console.log(response.json);  
  50.     }, error => {  
  51.       console.log('An unexpected error occured');  
  52.       console.log(error);  
  53.     })  
  54.   }  
  55.   
  56.   deletePost(post)  
  57.   {  
  58.     this.service.deletePost(post.id)  
  59.     .subscribe(response=> {  
  60.       let index = this.posts.indexOf(post);  
  61.       this.posts.splice(index, 1);  
  62.       console.log(response.json);  
  63.     }, (error: Response) => {  
  64.       if(error.status == 404)  
  65.         console.log('Already deleted')  
  66.       else {  
  67.         console.log('An unexpected error occured');  
  68.         console.log(error);  
  69.       }  
  70.         
  71.     })  
  72.   }   
  73. }  
You can see the below error when you run the application:
 
Handling Errors In TypeScript
 
Change the URL to the correct one and test all other methods.
 
Now call create, update, delete request by giving the invalid values and find out what error are occurred that you can handle on your own. By displaying on toast message, by notification, by error message, or by any other alert mechanism. You can create the error logs by inserting the error messages in some standalone file or in database. It is all up to you what information you want to keep or using when error occurred. In a large scale application, error handling is one of the most important mechanism that is being implemented so as to track the efficient working of the application by handling it in different ways programmatically.
  1. ngOnInit() {  
  2.     this.service.getPosts()  
  3.     .subscribe(response=> {  
  4.       this.posts = response.json();  
  5.     }, (error) => {  
  6.       console.log('An unexpected error occured');  
  7.       console.log(error);  
  8.     }, () => {  
  9.       console.log('completed');  
  10.     });  
  11.   }  
In the above method, we have one more optional parameter that indicates complete.
 
If the request is successfully completed, then it will execute. If an error occurred, then the error optional method will execute. You can go through details.
 
Is the implementation for error handling good enough?
 
The above implementation is not good enough, as we have so many ugly implementations in PostComponent related to handling the error through Response and error code. This should be checked on service and the application-specific error object should be thrown from the service.
 
Let's start implementing:
 
Add three error files app-error.ts, not-found-error.ts, bad-request-error.ts,
  1. export class AppError {  
  2.     constructor(originalError?: any) {          
  3.     }  
  4. }  
  5. import { AppError } from './app-error';  
  6. export class NotFoundError extends AppError{  
  7.         // define various operations in case if NotFoundError occured  
  8. }  
  9. import { AppError } from './app-error';  
  10. export class BadRequestError implements AppError{  
  11.     // define various operations in case if BadRequestError occured  
  12. }  
Modify PostService (post.service.ts):
  1. import { Http } from '@angular/http';  
  2. import { Injectable } from '@angular/core';  
  3. import { HttpErrorResponse } from '@angular/common/http';      
  4. import { throwError } from 'rxjs';   
  5. import { AppError } from '../common/app-error';  
  6. import { NotFoundError } from '../common/not-found-error';  
  7. import { BadRequestError } from '../common/bad-request-error';  
  8. import { catchError } from 'rxjs/operators';  
  9.   
  10. @Injectable({  
  11.   providedIn: 'root'  
  12. })  
  13. export class PostService {  
  14.   private url = 'http://jsonplaceholder.typicode.com/posts';  
  15.   constructor(private http: Http) { }  
  16.   
  17.   getPosts(){  
  18.     return this.http.get(this.url)  
  19.     .pipe(catchError(this.handleError));  
  20.   }  
  21.   createPost(post){  
  22.     return this.http.post(this.url, JSON.stringify(post))  
  23.     .pipe(catchError(this.handleError));  
  24.   }  
  25.   updatePost(post){  
  26.     return this.http.patch(this.url + '/' + post.id, JSON.stringify({isRead: true}))  
  27.     .pipe(catchError(this.handleError));  
  28.   }  
  29.   deletePost(id){  
  30.     return this.http.delete(this.url + '/' + id)  
  31.     .pipe(catchError(this.handleError));  
  32.   }  
  33.   handleError(error: HttpErrorResponse){  
  34.     if(error.status == 400)  
  35.       return throwError(new BadRequestError());  
  36.     if(error.status == 404)  
  37.       return throwError(new NotFoundError());  
  38.     else  
  39.       return throwError(new AppError());  
  40.     }  
  41. }  
Modify PostComponent (post.component.ts):
  1. import { Component, OnInit } from '@angular/core';  
  2. import { PostService } from '../services/post.service';  
  3. import { NotFoundError } from '../common/not-found-error';  
  4. import { AppError } from '../common/app-error';  
  5. import { BadRequestError } from '../common/bad-request-error';  
  6.   
  7. @Component({  
  8.   selector: 'app-posts',  
  9.   templateUrl: './posts.component.html',  
  10.   styleUrls: ['./posts.component.css']  
  11. })  
  12. export class PostsComponent implements OnInit {    
  13.  posts: any[];  
  14.   constructor(private service: PostService)   
  15.   {      
  16.   }  
  17.   
  18.   ngOnInit() {  
  19.     this.service.getPosts()  
  20.     .subscribe(response=> {  
  21.       this.posts = response.json();  
  22.     }, (error: AppError) => {  
  23.       if(error instanceof NotFoundError)  
  24.         console.log('Not Found Error');  
  25.       else if(error instanceof BadRequestError)  
  26.         console.log('Bad Request Error');  
  27.       else  
  28.         console.log('App Error');              
  29.     }, () => {  
  30.       console.log('completed');  
  31.     });  
  32.   }  
  33.   
  34.   createPost(input: HTMLInputElement)  
  35.   {  
  36.     let post = {title: input.value};  
  37.     input.value='';  
  38.     this.service.createPost(post)  
  39.     .subscribe(response => {  
  40.       post['id'] = response.json().id;  
  41.       this.posts.splice(0,0,post);        
  42.     }, (error: AppError) => {  
  43.       if(error instanceof NotFoundError)  
  44.         console.log('Not Found Error');  
  45.       else if(error instanceof BadRequestError)  
  46.         console.log('Bad Request Error');  
  47.       else  
  48.         console.log('App Error');              
  49.     });  
  50.   }  
  51.   
  52.   updatePost(post)  
  53.   {  
  54.     this.service.updatePost(post)  
  55.     .subscribe(response=> {  
  56.       console.log(response.json);  
  57.     }, (error: AppError) => {  
  58.       if(error instanceof NotFoundError)  
  59.         console.log('Not Found Error');  
  60.       else if(error instanceof BadRequestError)  
  61.         console.log('Bad Request Error');  
  62.       else  
  63.         console.log('App Error');              
  64.     })  
  65.   }  
  66.   
  67.   deletePost(post)  
  68.   {  
  69.     this.service.deletePost(post.id)  
  70.     .subscribe(response=> {  
  71.       let index = this.posts.indexOf(post);  
  72.       this.posts.splice(index, 1);  
  73.       console.log(response.json);  
  74.     }, (error: AppError) => {  
  75.       if(error instanceof NotFoundError)  
  76.         console.log('Not Found Error');  
  77.       else if(error instanceof BadRequestError)  
  78.         console.log('Bad Request Error');  
  79.       else  
  80.         console.log('App Error');              
  81.     })  
  82.   }   
  83. }  
Now you will get the application specific error. You can define the error message and status code and use that application specific error object to use further in our component. Instead of checking for Response error and check for error status and then defining the error usage, we should add an application-specific error handling mechanism and use them in our application.
 
Thank you!