Create custom Observables by wrapping existing APIs in Angular 7
Introduction
Hi folks! We have already jumped together in my previous article into the hot topic of Observables. Purpose of this blog post is to demonstrate examples on how to write your own custom Observables in Angular 7. More specifically by reaching the end of this post you will be able to wrap methods of an existing API in an Observable stream sequence. Geolocation API and Distance Matrix API will be used as examples. The described approach can be implemented in any other modern javascript framework.
Architecting Observables in Angular
There are online some discussions on how to create custom Observables. However it is not always clear how to wrap an api in it. Even if there are resources on how to wrap an existing api with Rxjs it is not explained where the Observable should be located within the architecture of a web application. Purpose of this section is to clearly describe the way a custom Observable wraps an external API as well as how this will be orchestrated within the overall architecture.
There will be two phases: a) the creation/publishing phase of an Observable and b) its subscription phase during which the emitted values are retrieved. There are different approaches regarding how data can be managed in a web application. This article is scoped towards the “ Observables Data Services with Rxjs’ approach on architecting a frontend application. Alternative approaches in Angular are Ngrx/store with NgrxEffects or Angular/redux. In other frameworks such as React or Vue React Redux and Vuex are used respectively. Depending on which one we follow different files are used to host the creation of the Observables. In our scoped approach the creation is placed in “services” files, while in the later approaches they are placed in “effects” or “action” files. Our service file will be tight to the specific component. In the central-state approaches the observables will be kept in shared, central location within a structure called “store”. Regarding the subscription phase it always resides within the templates of the components.
Observables Data Services and Rxjs
Data in frontend applications are received and handled in structures mostly called services and from there they are transmitted to the application. Services can be tight to a specific component, to multiple components or can be globally used. They are classes that implement the whole business logic of our apps.
Service as Observable’s Publisher
In order to create an Observable we need to place it in a publisher. A service will act as one. Within the service class we create a method “watchAPI” which will return the Observable. The snippet below is pseudocode abstracted to work for any external api calls.
First and foremost we need the Rx.Observable.create(callback) operator. The latter is passed as an argument a function who takes as an parameter the Observer object. Once we have the observer object and before making any call to the external api we might need to do some sanity checks i.e. to check if geolocation object exists in the current browser or to initialise the configuration object before sending it to DistanceMatrix service. After we check all these we can make the call to the external API and within its success and fail callbacks we run the respective Observer.next(value) or Observer.onError(obj) methods. The Observer.next(value) is called when the Observable successfully “sends values from the external api” or else publishes them. The Observer.onError is called in the case of a failed attempt. The object passed as parameter in this case is the error object which can carry with it some useful information for debugging such as error code, error message etc. The Observer.complete() is called when an Observable does not have any other values to push.
Component as Observable’s Subscriber
Once the observable has been published it should be subscribed in order to be consumed. There are two different ways to do this:
Self subscription in component’s logic
In this approach we need manually to subscribe to the observable and to unsubscribe it when the component is destroyed. Although it is not a very automated way, it is recommended in cases where data manipulation is needed before their rendering in the template. What is actually data manipulation? Data manipulation is the process of modifying the retrieved results before using them. Such a modification could be the filtering of the data according to a criterium. Quite often in API’s the retrieved data are returned in a json object along with some additional metadata. In such cases we need to extract the core data from the rest information by selecting from the retrieved json only the core data. In its simplest form data manipulation is projection of retrieved values to local variables. In the following code the map Rxjs operator is used to apply projection with each value from source . In the specific case we didn’t actually modified the data, we projected their values to the private property of the component’s class in order it to be rendered in the template.
Subscription in the template’s logic with async pipe
In this approach, angular is aligned with the life cycle of the component which is automatically updated when new data has been received. You don’t need to manually subscribe and unsubscribe the observable. Only restriction of this approach is that you cannot manipulate the data.
Geolocation API (publisher)
Geolocation API is a Web API that is used to provide the location of the users to web applications. The “getCurrentPosition” method specifically of the aforementioned API is used for user’s device location retrieval. How can this be converted into an Observable? In a few steps:
- Within a method of our service (getCurrentLocation) we return the custom Observable. We can think getCurrentLocation method as a replacement for the watchAPI used in the previous chapter.
- Within the custom Observable we wrap external’s api method by executing it
- The Observer Object that was obtained in the first step, is used in the results of the second step to push/send the data to the subscriber.
All the game begins when we have access to the observer object. First we do a sanity test to check the existence of the navigator object and its child geolocation. Then follows the execution of the external api method (getCurrentPosition). The latter accepts two callback functions as arguments. Whatever code will be placed in the first callback is a successful case, while whatever will be placed in the second one is the error case. In the successful case the retrieved data (position in this example) are passed as an argument to the callback. These retrieved data (position) are used in turn in Observer.next(position) command.
Distance Matrix API (publisher)
Distance Matrix API is a google API that is used to calculate the travel distance and time for a matrix of origins and destinations. This is calculated based on the recommended route between start and end points. In the screenshot below you can see my implementation of it as an Observable stream. Similar steps have been followed as in previous api:
- We create a wrapper method in our service which returns a custom Observable. We can think getDistanceMatrix wrapper method as a replacement for the watchAPI used in the previous chapter.
- At this step some preparation needs to be done before executing the external api within the custom Observable. During the preparation steps a configuration object is initialised. The latter contains information about the travelMode, origins, destinations etc. Some of these properties are calculated based on the arguments (currentPosition,restaurant) passed on to the wrapper method. Depending on the context in which the outer wrapper method will be executed (in subscription in component’s logic) we have access to the values of these arguments. Lastly, in order to execute the external api’s getDistanceMatrix method (which is passed the aforementioned configuration object) we need to initialise a Distant MatrixService.
- The Observer Object that was obtained in the first step, is used in the results of the second step to push/send the data to the subscriber.
Geolocation + Distance Matrix (Subscriber)
After having published the custom Observables for the two API’s we subscribe them in the template’s application logic. In the screenshot below you can see how with chained Rxjs operators (map, switchMap) the data are manipulated.
In the first two lines we can see the assignment of Observables to variables. There is a common naming convention to use $ at the end of an Observable variable. Pipe is a method of Observable and is used to combine functional operators and Share is used to share source across multiple subscribers.
There are 3 Observables on the above screenshot among of which the 2 are the ones we described in the previous sections. The getRestaurants$ is an Observable returned from angular’s http request and it returns a list of restaurants. Once the restaurants are retrieved and projected with the map operator, then they are manipulated in a chain with various operators (switchMap, map), among of which the getLocation$ Observable is consumed. Depending on the application logic of our app we chain operators, manipulated Observables’s retrieved data, we chain one Observable inside the other (getDistanceMatrix) and once the data are calculated in the format we want, we are ready to subscribed the response.
The above calculation with the operators could take place alternatively in the service in the getRestaurants method.