by Tomas Trajan

⚡ How to never repeat the same RxJs mistakes again⚡

Remember: .pipe() is not .subscribe()!

Look! A lightning tip! (Original ? by Max Bender)
This article is directed at the beginners trying to increase their RxJs knowledge but can also be a quick refresh or a reference to show to beginners for more experienced developers!

Today we are going to keep it short and straight to the point!

Currently I am working in a rather large organization quite a few teams and projects (more than 40 SPAs) that are in the process of migration to Angular and therefore also RxJs.

This represents a great opportunity to get in touch with the confusing parts of RxJs which can be easy to forget once one masters the APIs and focuses on the implementation of the features instead.

The “.subscribe()” function

RxJs observable represent a “recipe” of what we want to happen. It’s declarative which means that all the operations and transformations are specified in their entirety from the get go.

An example of an observable stream could look something like this…

Example of the RxJs observable stream declaration

This RxJs observable stream will do literally nothing by itself. To execute it we have to subscribe to it somewhere in our codebase!

This subscription will log our greetings every odd minute

In the example above, we provided a handler only for the values emitted by the observable. The subscribe function itself accepts up to three different argument to the handle next value, error or complete event.

Besides that we could also pass in an object with the properties listed above. Such an object is an implementation of the Observer interface. The advantage of observer is that we don’t have to provide implementation or at least a null placeholder for the handlers we are not interested in.

Consider the following example…


In the code above, we are passing an object literal which contains only complete handler, the normal values will be ignored and errors will bubble up the stack.


And in this example, we are passing the handler of the next error and complete it as direct arguments of the subscribe function. All unimplemented handlers have to be passed as a null or undefined until we get to the argument we’re interested in.

As we can see, the inline argument style of implementation of a .subscribe() function call is positional.

In my experience, the inline arguments style is the one which is most common in various projects and organizations.

Unfortunately, many times we may encounter implementation like the following…

Example of redundant handlers often encountered in the “wild”

The example above contains redundant handlers for both next and error handlers which do exactly nothing and could have been replaced by null.

Even better would be to pass the observer object with the complete handler implementation, omitting other handlers altogether!

The “.pipe()” and the operators

As beginners are used to providing three arguments to subscribe, they often try to implement a similar pattern when using similar operators in the pipe chain.

RxJs operators, which are often confused with the .subscribe() handlers, are catchError and finalize. They both serve a similar purpose too — the only difference being that they are used in the context of the pipe instead of the subscription.

In case we would like to react to the complete event of every subscription of the RxJs observable stream, we could implement finalize operator as a part of the observable stream itself.

That way we don’t have to depend on the developers to implement complete handlers in the every single .subscribe() call. Remember, the observable stream can be subscribed to more than once!
Use the finalize operator to react to the complete event of the stream independently from the subscription. (Similar to tap)

This brings us to the final and arguably most problematic pattern we may encounter when exploring various code bases: redundant operators added when trying to follow .subscribe() pattern in the .pipe() context.


Also, we might encounter its even more verbose cousin…

Stuff might get verbose…

Notice we have progressed from the original single line to the full nine lines of code which we have to read and understand when we want to fix a bug or add a new feature.

Stuff might get even more complex when combined with more complex generic Typescript types, which can make the whole code block even more mysterious (and hence waste more of our time).


  1. The .subscribe() method accepts both the observer object and inline handlers.
  2. The observer object represents the most versatile and concise way to subscribe to an observable stream.
  3. In case we want to go with the inline subscribe arguments (next, error, complete) we can provide null in place of a handler we don’t need.
  4. We should make sure that we don’t try to repeat the .subscribe() pattern when dealing with .pipe() and operators.
  5. Always strive to keep the code as simple as possible and remove unnecessary redundancies!

That’s it! ✨

I hope you enjoyed this article and will now have better understanding of how to subscribe to RxJs observables with clean, concise implementation!

Please support this guide with your ??? using the clap button and help it spread to a wider audience ? Also, don’t hesitate to ping me if you have any questions using the article responses or Twitter DMs @tomastrajan.

And never forget, the future is bright
Obviously the bright future! (? by Xavier Coiffic)
Starting an Angular project? Check out Angular NgRx Material Starter!
Angular NgRx Material Starter with built in best practices, theming and much more!

If you made it this far, feel free to check out some of my other articles about Angular and frontend software development in general…

?‍?️ The 7 Pro Tips To Get Productive With Angular CLI & Schematics ?
Angular Schematics is a workflow tool for the modern web — official introduction  The Best Way To Unsubscribe RxJS Observable In The Angular Applications!
There are many different ways how to handle RxJS subscriptions in Angular applications and we’re going to explore their…blog.angularindepth.comTotal Guide To Angular 6+ Dependency Injection — providedIn vs providers:[ ] ?
Let’s learn when and how to use new better Angular 6+ dependency injection mechanism with new providedIn syntax to make… The Ultimate Answer To The Very Common Angular Question: subscribe() vs | async Pipe
Most of the popular Angular state management libraries like NgRx expose application state in a form of a stream of…