In this article, I will show you how to create a generic solution to Consume REST API in Angular. I will utilize Typescript Generics in combination with Angular HTTPClient service to eliminate any code redundant, be as DRY as possible, and follow the Open–closed principle.
Most applications need to communicate with a remote server over the HTTP protocol, in order to perform the basic CRUD operations. With Angular, you can use
HTTPClient service to achieve this communication easily. As an example, if you need to manage the Posts of your blog, you may have the following service to handle all the operations on the Post resource:
This solution is simple and clean, and it even follows the best practices according to the official Angular Documentation. However, applications usually have many resources to manage, for our example, we may have users, comments, reviews, etc. Ideally, each of these resources should have a separate service to handle CRUD operations and communicate with the server, at the end we will have UserService, CommentService, ReviewService. Let's take a look at how the CommentService would look like:
Although the above implementation is very common and widely acceptable, it suffers from two cons:
- Code redundant (breaking of the DRY principle): If you compare the
CommentServiceyou will notice how redundant the code is.
- Changes in the server-side, or changes in the way to communicate to the server, require changes in many files (in our case we need to change both
To solve the above issues let's go ahead and build the following abstract class which will be the base of all the other services:
- The new service class is
abstract, which means it can't be instantiated and used directly, but it needs to be extended by other classes.
- We provide one abstract method
getResourceUrl, The class which extends this abstract class must implement this method, and return the URL of the resource as we will see in the following section.
- This is a Generic Class, it is not tied to a specific type, rather the class which extends this abstract class will define the exact type used.
- It has all the needed CRUD operations which we need and used before in the previous service.
Now after we have our abstract generic class, whenever we need a new service we can simply extend this class and implement the only one abstract method
getResourceUrl. so the PostService and CommentService will be as the following:
In most applications, the front-end model doesn't match %100 the server-side model. In other words, the REST API will respond with json object that doesn't match exactly the interface or the class defined in the front-end application. In this case, you need a mapping function to convert between server and front-side mode. This sometimes referred to as serializing/deserializing.
So, let us extend our base class to provide this mapping functionality. To do so I updated the
ResourceService to look as the following:
- I added two new methods:
toServerModel: to convert from the Front-end model to the Server Model, It accepts the resource generic type
fromServerModel: to convert from the Server model to the Front-end Model, it accepts a parameter of the type
anywhich represent the server response, and return the generic type
- I provided a default implementation for both of the two methods
fromServerModel, so in case no mapping needed, the same object returned by the server will be used as a front-end model. Also since I added a default implementation, the consumer of this service doesn't have to override or even implement these two methods at all.
- In both
getmethods, I am using the new method
fromServerModel, to map the server response to the front-end Model.
- In both
updatemethods, I am using
toServerModelto map the front-model to the server model before posting the data to the server.
Now to consume the new changes we have two cases:
- There is no mapping needed between the server and the front-end model, in this case, we don't have to change anything in the class that extends the
- There is some kind of mapping needed between the server and the front-end model, all we need to do is to override
fromServerModelmodels in the derived class to address our requirement mappings. For example let's assume, that the
PostsServiceimplemented previously needs to map from timestamp to a js Date object, the PostsService implementation would look like the following:
To communicate with a server using the HTTP protocol, you need to use the Angular HTTPClient service. In this article, we implemented a generic extendable solution to allow us to achieve this communication. Our solution is clean, DRY, and follows the Open–closed principle. We utilized Typescrip Generics, Generic Classes, and we even took into consideration a required mapping between server and front-end model.