How to map REST API data using Decorator pattern in Angular 6
A cleaner, loosely coupled, and more maintainable solution
To build a solid RESTful API, there are well-established best practices and design patterns we can use. In this article, I will talk about how to use the Decorator pattern to consume RESTful API in Angular.
HttpClient Service
To consume a REST API in Angular, we can use HttpClient . HttpClient Service handles the JSON data parsing under the cover and returns an Rxjs observable. It also supports generic typing functionality. For example, in the code snippet below, getAllTodos will return an observable with an array of objects that match the type TodoModel.
1 | constructor(private http: HttpClient) {} |
It is straightforward to make use of HttpClient to call the REST API and get the data to bind to the view.
The Problem
All seems good so far, but problems start to arise when the back end changes. Let’s say we have the TodoModel as below.
1 | export class TodoModel{ |
Two changes are required
We want to display a text description of “completed” when the value is true and display “pending” if the value is false.
The field name is changed from “name” to “title”.
The quick way to meet the requirements is to change the model.
1 | export class TodoModel{ |
Then change the todo service to map the completed field.
1 | getAllTodos(): Observable<TodoModel[]> { |
In the end, we also need to change the binding at the view to “title” instead of “name”.
It works, but not ideal.
Firstly, it is a simple change: rename one field in the backend. But this simple change requires updating the service, domain model, and view, it is an indication of bad design. Secondly, we may need to repeat the explicit mapping of data to every method that needs to retrieve TodoModel from the back end, which will pollute the code in the long term.
Decorator Pattern
One way to avoid the above problem is to use the Decorator pattern.
Decorator pattern allows you to change the behavior of an object, without changing the original object. This is done by wrapping the original object with a decorator that implements the same interface but adds behavior and/or modifies input and output.
As the diagram illustrates, for the Decorator Pattern, we are not using inheritance to add functionality. Instead, we use composition. It is a more loose coupled pattern.
In Typescript, the decorator is simply a function and will be called at run time. Different types of Decorators can be applied to different levels like Class Decorator, Method Decorator, or Property Decorator.
Here, we use a custom Property Decorator to handle the REST API Data mapping.
StatusConverter Decorator
Here is how we use the “statusConverter” decorator to handle transforming the “complete” status from Boolean to string.
A property decorator takes two arguments:
target: the prototype of the class
key: the name of the property
The below decorator function contains a getter and setter accessors, so we can manipulate the property value as required.
1 | export function statusConverter(target: any, key: string) |
A property decorator is declared just before a property decoration.
1 | @statusConverter |
That is it, no need to manually set the property value in the service code, and We can apply the same decorator everywhere when necessary.
In this case, the use of the statusConverter decorator is very limited, it only converts the true value to “Completed” and the false to “Pending”. However, it is for illustration only, there is no limitation on how the data can be transformed.
PropertyMap Decorator
To handle the case of backend data field renaming, we created the PropertyMap decorator and ModelMapper utility class.
The PropertyMap decorator provides a way to store the meta-data.
To consume the metadata, ModelMapper class is added.
Now we have a ModelMapper class that can take any type, extract the predefined metadata, and performs data transformation.
To use the decorator
1 | @propertyMap('title') |
The updated service looks like the one below.
1 | return this.http.get<TodoModel[]>(this.url).pipe( |
Further refactoring
After we start to make use of the ModelMapper for the services, we will soon find that the code is duplicated in every service. Thus we can extract the modelMapper to a generic API Service class as the code snippet below.
1 | // api.service.ts |
Benefits
With the above decorators, when the back-end data field is renamed or changed, there is no need to change the service code. The only change required is to update the PropertyMap decorator meta-data argument in the model class.
No matter how many services consume the TodoModel API, only a single change is required to apply it to all services.
The code also becomes more readable as the name of the decorator shows the intent of the function clearly.
Be declarative
Using the Decorator pattern is a practice of declarative programming, it focuses on how the components/services want to behave instead of how to accomplish the result. The declarative approach helps developers to produce a cleaner, loosely coupled, and more maintainable code base.
The full demo code is available at this stackblitz project .
Happy Programming!
- Title: How to map REST API data using Decorator pattern in Angular 6
- Author: Sunny Sun
- Created at : 2018-12-11 00:00:00
- Updated at : 2024-07-07 18:34:05
- Link: http://coffeethinkcode.com/2018/12/11/how-to-map-restful_api-using-decorator/
- License: This work is licensed under CC BY-NC-SA 4.0.