DEV Community

Cover image for Exploring reactive programming with Node.js
Matt Angelosanto for LogRocket

Posted on • Originally published at blog.logrocket.com

Exploring reactive programming with Node.js

Written by Emmanuel John ✏️

Introduction

Reactive programming provides advanced data flows with the ability to create and manipulate event streams in a predictable manner.

This article will teach Node.js developers how to apply reactive programming in Node as well as its benefits and trade-offs.

The following will be covered in this article:

  1. Fundamentals of reactive programming
  2. Why consider reactive programming in Node.js?
  3. When to use the reactive programming approach
  4. Benefits of reactive programming
  5. Drawbacks of reactive programming
  6. Introducing orchestration and its benefits/trade-offs
  7. Reactive programming libraries for Node

What is reactive programming?

In simplified terms, a program is said to be reactive when an input change leads to a corresponding change in output without any need to update the output change manually. This allows software engineers to bypass the stress involved with handling huge implementations manually.

The functional reactive programming paradigm allows our reactive codebase to be read and understood easily since it reduces callback hell, which makes asynchronous blocks of code difficult to read.

Since reactive programming has much to do with asynchronous operations, the functional approach makes it easier to determine the outcome of asynchronous operations.

Fundamentals of reactive programming

Operators

Operators are methods that Observables rely heavily upon. They have the following use cases:

  • Transformation of asynchronous events to Observables when handling asynchronous requests
  • Combination of multiple sequences of Observables into a single Observable
  • Error handling
  • Handling time-based operations

Observable operators include the filter(...), mergeMap(...), of, from, concat methods, and so on.

Observable streams

An Observable stream is an array of multiple input values that are processed over time. An Observable stream emits events to its subscribers which in turn, listen to these events for further processing. Observable streams can be combined to create new streams. Array methods such as map, reduce, filter, and so on are used to manipulate the streams.

Values can be emitted to the subscriber as follows:

import { of, Observable } from "rxjs"; 
const emitter : Observable<string> = of("Sam", "Ray", "Thomas");
Enter fullscreen mode Exit fullscreen mode

Subscribers

Observable subscribers are more like array iterators. They loop through the resulting Observable streams, making it possible to transform or process each stream.

The snippet below shows how to subscribe to an Observable stream:

emitter.subscribe((value: string) => {
  console.log(`Name: ${value}`)
})
Enter fullscreen mode Exit fullscreen mode

Reactive programming has some inbuilt subscription methods such as the emit and the flatMap map methods, which allow us to listen to each value of an Observable stream and process them to fit our needs.

Criteria for reactive systems

A fully reactive Node.js system should meet the following criteria:

Responsive architecture

A reactive system should possess a great user experience by providing timely responses to user interactions.

Resilient architecture

Resilient architecture, if correctly implemented, will allow the system to respond to errors without breaking the entire system.

This architecture ensures that each node has a replica. In case the main node goes down, there will be some sort of fallback on other available nodes.

Scalable

The system should be capable of handling varying loads, which has to do with its ability to downscale when infrastructure requires little or no resources, and scaling up when the infrastructure requires more resources so as to provide an efficient cost management strategy.

Also, the system should be able to handle point-in-time loads as well.

Why consider reactive programming in Node.js?

Now that we have discussed briefly the fundamentals of reactive programming, it is also important that know the reasons for considering the reactive approach to programming with Node.js.

Scalability

Writing functional reactive code makes it easier to manage a codebase and improve the scalability of the project.

Feature Implementation

For projects that require changes to features or the addition of new features regularly, writing functional reactive code makes it easier for new features to be added to an existing project.

Time-related intricacies

When making asynchronous requests to external APIs, we do experience some time-limiting constraints. These constraints can be efficiently handled with the reactive approach to programming.

Reduction of code verbosity

Implementing reactive programming paradigms will drastically reduce the amount of code required to implement a given feature.

Introducing orchestration and its benefits/trade-offs

Before the inception of reactive programming, building microservices with Node.js required the orchestrator pattern for coordination of all the service interactions.

A typical use case of an orchestrator pattern is having microservices in an ecommerce application that handles the following tasks sequentially: take customer orders from the cart, calculate the total amount, generate a bill, and after successful payment, update the product inventory and create an order ID with a Pending status to the seller.

While this provides a systematic way to handle the logical flow of the application, a major drawback of tight coupling of dependencies can break down the entire system. For instance, if a preceding service is down, then all the dependent services will not be executed.

When to use reactive programming approach in Node.js

Reactive programming is not a one-size-fits-all approach, but it has some specific situations where is a great fit:

  • Reactive programming pattern is a great fit where there is a need for decentralization of the application flow into manageable microservices
  • When there is limited time to ship an application to production
  • When a temporary shutoff in a preceding dependency can lead to the breakdown of the entire system
  • Reactive programming is also a great fit when there are lots of asynchronous blocks of code and the awaiting results could be delayed

Drawbacks of reactive programming in Node.js

Although the functional reactive programming approach reduces the drawbacks encountered with the orchestrator pattern, it cannot replace the orchestrator pattern because it has its own drawbacks:

  • Redundant code blocks resulting from breaking down the application flow and distributing across all the services
  • A thorough understanding of streams and event loops is required in order to build reactive services

Popular reactive programming libraries in Node.js

RxJS

This is one of the most popular reactive programming libraries in JavaScript that is actively maintained.

At the time of writing, RxJS is in transition from v7 to v8, and it had more than 27 million downloads in the last week. The transition features a rewrite of the library for great performance, better modularity, better debuggable call stacks, and backward compatibility.

Here's a quick example of RxJS usage:

import { range } from "rxjs";
import { map, filter } from "rxjs/operators";

range(1, 200)
  .pipe(
    filter(result => result % 2 === 1),
    map(result => result * 2 )
  )
  .subscribe(result => console.log(result));
Enter fullscreen mode Exit fullscreen mode

Reactor.js

Reactor.js is another JavaScript library for reactive programming. Although it’s not quite popular compared to Bacon.js and Rxjs, it is known for its light weight. It is a lot easier to maintain consistency in complex data models using Reactor.js because it automatically tracks reactive variables and retriggers the observers if the value of any reactive variable is changed. With Reactor.js, there is no need to manually set subscriptions/listeners because dependencies are set for you automatically.

Here's a quick example of Reactor.js usage:

const reactor = new Reactor({ name: "Doe" });

observe(() => {
  console.log("My name is ", reactor.name);
}); // prints "My name is Doe"

reactor.name = "John "; // prints "My name is John"
Enter fullscreen mode Exit fullscreen mode

Reactor is based on the same reactive principles as Bacon.js and Knockout.js.

Other JavaScript libraries for reactive programming include:

  • Flyd
  • Bacon.js
  • Knockout.js
  • Kefir
  • Most

Conclusion

In this article, we’ve explored reactive programming, its benefits, and when it is best fitted for our Node.js projects. Also, we’ve discussed orchestration, its benefits/trade-offs, and JavaScript libraries for reactive programming in Node.js.

Hopefully you’ve found this post informative and helpful.


200’s only ✔️ Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, try LogRocket.

LogRocket Network Request Monitoring

LogRocket is like a DVR for web apps, recording literally everything that happens on your site. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.

LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.

Top comments (0)