Ground control to major Tom — HTTP calls in RxJS

Jurek Wozniak
6 min readSep 21, 2021

--

Modern apps make a ton of HTTP requests, very often concurrent. RxJS is all about handling streams, reacting to values, performing asynchronous actions, so it seems to be a great tool to use in the HTTP area. What options does RxJS give us as far as handling HTTP requests is concerned?

Photo by NASA on Unsplash

XMLHttpRequest and fetch()

Let’s start with what options do we actually have available in the browser.

The XMLHttpRequest (in short — XHR), which dates back to at least 2006, allows us to make HTTP calls. Using it is not the most convenient thing in the world, but it did and still does the job in most cases. To make it more friendly, many easy-to-use wrappers have been created, including the super-popular axios.

Another, more modern option is the Fetch API and its fetch() method returning a Promise, which is a way more convenient interface.

The differences

The fetch() method returns a Promise, which resolves when the HTTP call succeeds and rejects if there is an error. This is very easy to use:

Using the XMLHttpRequest (XHR) has plenty of features, but is less convenient to use. See the code:

This is the main difference — nowadays we try to use Promises and async/await syntax to conveniently describe asynchronous code. The callback solution of the XHR is harder to follow in more complex scenarios.

Another difference is that an XHR call provides an easy way to know the progress, which is useful when we download or send a bigger file and want to present the progress to the user.

On the other hand, the fetch() method provides a way to get a ReadableStream and process the incoming data in chunks, which might be useful when dealing with huge amounts of data.

Sometimes we want to cancel the request when we want to stop sending or fetching some file or we do not care about the response anymore.
This is easily accessible when using XHR. You can call: xhr.abort();
Previously, the fetch() method, due to using a Promise, was not cancellable, however a special signal using AbortController was added, which allows to cancel calls made by fetch().

Some might take browser support into consideration, as the Fetch API is newer, and while it is supported by all modern browsers, this API still gets updated, so it is best to check if all used features are supported by needed browsers.

What about RxJS?

As you can see, the two ways which are available have very different interface and the way we interact with them. The fetch() method, while being more convenient to use does not include all features of the less convenient XMLHttpRequest.

Here comes the RxJS with help! The Observables are a great way to wrap the above HTTP interfaces. The best news is that RxJS provides many tools to conveniently use both of them!

I have shortly explained the available built-in HTTP interfaces as they are crucial to understand the tools provided by RxJS.

RxJS provides two helper functions returning Observables performing HTTP calls:

  • ajax() — using XMLHttpRequest
  • fromFetch() — using fetch()

Note: Angular developers will most likely use the built-in HttpClient service, which also uses RxJS and Observables and serves as a great way to handle HTTP calls. This article will focus on the features built into RxJS and not cover the HttpClient service.

ajax()

The ajax() creation function wraps an XMLHttpRequest (XHR) and gives us an Observable.

Performing a simple GET operation is super easy:

The great thing is that if the response is a JSON object, it will automatically be parsed and provided to us as a regular JavaScript object.

Thanks to this wrapping into an Observable, you get the convenient error handling and composability — the same as with any other Observable:

fromFetch()

The fromFetch() creation function returns an Observable built on top of the fetch() call using the Fetch API.

Performing a simple GET call is also easy, however it does handle things in a more low-level fashion. Let’s consider the following code fetching some JSON response from an endpoint:

Notice the selector option provided to fromFetch(). This is used to tell which part of the response do we want to get. It is useful, because if we would like to do something similar to the above code without using the selector field, it would look more like this:

Examples — fromFetch vs ajax

Let’s now compare how to achieve various scenarios when using each function.

POST request — ajax()

POST request — fromFetch()

Cancellation

If we want to abort an HTTP call, thanks to RxJS, it is super simple in both cases:

Just unsubscribe — the rest is handled for us by RxJS.

Progress reporting

This one is easily available when using ajax() only.

If you are interested in the progress of a file you are downloading:

You can also use it for tracking the upload progress.

The event.type above can be related to upload_ or download_ and can describe startload, progress or load events. The type is created by combining the prefix with the event. For example, if you are interested in the progress event of the upload process, the event type you are looking for is 'upload_progress'.

It is possible to achieve something similar when using fromFetch(), but it is more complex and not available out-of-the-box.

Timeout

Thanks to RxJS, this works the same for both.

The above will emit an error if the HTTP call does not respond within 10 seconds.

It works the same for the Observable made with fromFetch, so just replace the ajax() call with fromFetch() and it will work the same.

Streaming

Sometimes we want to handle the incoming data in batches and not wait for the response to end and load everything into memory.

We can do this by using the Observable made with fromFetch.

The selector uses the response.body, which returns a ReadableStream. RxJS will use this stream and emit the incoming values, which we can then can process further, for example in the handler inside of the .subscribe(...) call.

Providing default fallback value

In many case it is worth to provide some default response if the actual call fails. For that, use the catchError operator.

Bonus: Autocomplete

A great way to use HTTP calls in RxJS is to put them and use somewhere inside of the whole Observable stream.

For example, imagine that there is an input text field, where the user can type in the search query. Our code will fetch the autocomplete suggestions from the server as the user starts and continues to type and modify the query.

The above code will react each time the user updates the query input, debounce the events if the user types a lot of letters fast, and send a new HTTP request to the server.

Also, if the request takes a bit longer and a new search query will be provided by the user, the previous HTTP request will be cancelled and a new HTTP call will be made — this is thanks to the switchMap operator, which makes sure that only a single call is in progress at once and cancels the previous ones if there is new query coming from above in the pipeline of operators.

You can try it yourself in the following sandbox:

Start typing in the name of a drink in the “Search query” field. As soon as you stop for more than 500ms, a request will be sent to the Cocktail DB API. Then, the response will be mapped into the names of the drinks. Finally, these names will be presented as a list below the query input.

BTW, please note the crossDomain: true ajax() setting which might be sometimes needed — depending on the configuration of the server you are calling — so the browser can perform a successful CORS (Cross-Origin Resource Sharing) call. A CORS request is when the domain of your website is different than the one you are trying to reach.

Go ahead and experiment, change the code. Have fun!

Summary

As you can see RxJS provides great ways to incorporate HTTP calls in a convenient and standardized way. Depending on the features needed in your particular scenario and personal preference, the ajax() and fromFetch() functions are at your disposal. For more features and configuration options, see the docs linked below. Keep experimenting and have fun!

If you have enjoyed this article, please give it a clap and follow me: Medium, Twitter, LinkedIn. Thank you!

Useful links

New to RxJS?

If you are new to RxJS or would like to polish your knowledge, I invite you to have a look at my RxJS and Observables: Introduction course.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Jurek Wozniak
Jurek Wozniak

Written by Jurek Wozniak

Passionate about front-end software development.

Responses (2)

Write a response