Understanding switchMap and forkJoin operators in RxJS with TypeScript
Tags: rxjs, typescript, webdev
Published: 2020-12-17 22:54:02

RxJS is a library with more than 22 Million downloads per week. It's widely used in the JavaScript world, and supports TypeScript as well. 

I recently saw the following question in a developer forum:

> Is there a way to handle a list of _Observables_ and get the final result of them once they're completed?

Right after reading that question, I started to think in a _reactive approach_ in time to come up with a "pattern" that can be used in various scenarios.

### The Problem
Let's suppose you're building an application that needs to handle a series of asynchronous calls to display the users, and their respective addresses.

![user-addresses](//images.ctfassets.net/zojzzdop0fzx/FcsaVGBcN2sIZMpAKWG2f/88a8f04b81f060fbfc3490e35ca71973/user-addresses.png)

However, in a real-world scenario, the system can provide a RESTful service to get the list of the Users(ie. `getUsers()`).

![users](//images.ctfassets.net/zojzzdop0fzx/2r74mU0H0CQ32UyS7UXQWJ/1a54a6d219430e4103f4cafbac5a098f/users.png)

Then, you'll need to perform another HTTP call to get the address for each of the previous users (i.e. `getAddress(userId)`).

![address-by-user-id](//images.ctfassets.net/zojzzdop0fzx/5tZiQnrur0d17aHjZDN0PV/d2731c203b60e72c83a1fd97929eb014/address-by-user-id.png)

Let's solve this problem in a _Reactive Approach_ using RxJS operators and TypeScript.

### Define the Data Model
Let's rely on TypeScript _interfaces_ and powerful static typing to have the model ready.

```ts
// user.ts
export interface User {
  id: number;
  name: string;
  address?: Address;
}
```

The _User_ interface defines the `address` attribute as optional and that means it can be `undefined` if the user doesn't "contain" a valid address value yet.

```ts
// address.ts
export interface Address {
  country: string;
  state: string;
  city: string;
  street: string;
  zipCode: number;
}
```

The _Address_ interface displays a set of attributes that represents a complete address.

### The Data Example
To make testing easier, let's define a dataset for the entities we defined already in the model.

```ts
// index.t

import { Address, User } from "./address";
import { User } from "./user";

const users: User[] = [
  {
    id: 0,
    name: "Donald Mayfield"
  },
  {
    id: 1,
    name: "Jill J. Fritz"
  },
  {
    id: 2,
    name: "Terry Buttram"
  }
];

const address: Address[] = [
  {
    street: "2180 BELLFLOWER",
    country: "USA",
    state: "AL",
    city: "Madison",
    zipCode: 35064
  },
  {
    street: "845 ODOM ROAD, SUITE 200",
    country: "USA",
    state: "CA",
    city: "Los Angeles",
    zipCode: 90720
  },
  {
    street: "9025 QUEENS BLVD",
    country: "USA",
    state: "NY",
    city: "Queens",
    zipCode: 11355
  }
];
```

Looks like we'll need a function to get an Address given a User id.

```ts
// index.ts
const getAddress  = (userId: number) => of(address[userId]);
```

The previous function `getAddress` will return an Address as an _Observable_: `Observable<Address>`. This is possible by using the `of` operator from RxJS, which is a "Creation Operator" that converts the arguments(an `Address` object) to an observable sequence.

### Processing the Data as Observables
Since we have the data model defined, it is time to create some variables that allow us to "contain" the set of users, and addresses, as _Observables_:

```ts
// index.ts

let users$: Observable<User[]>;
let address$: Observable<Address[]>;
```

Next, let's assign the appropriate value to the `users$`variable:

```ts
users$ = of(users);
```

Again, the previous line is using the `of` operator to create an observable from the `users` array. 

In the real world, the "users" data will come from a RESTful endpoint, a JSON file, or any other function that can perform an asynchronous call. You may expect an _Observable_ as a result of that function or you can create it using an RxJS Operator.

### Using switchMap and forkJoin Operators

Now it's time to process the set of Users and perform the `getAddress(id)` call for every User:

```ts
address$ = users$.pipe(
  switchMap((users: User[]) => {
    // Iterate the 'users' array and perform a call to 'getAddress' 
    users.forEach(user => {
      const address = getAddress(user.id);
      // ...
    });
  })
);
```

What is happening here?

* **users$.pipe()**. This function call provides a readable way to use RxJS operators together(`switchMap` in this example).
* **switchMap()** operator comes first and allows to process the data(`User[]`) which comes from the observable(`Observable<User[]>`). Also, it allows **returning an Observable** as a result of applying `getAddress()` call for every user.

However, we're not doing anything useful yet from `getAddress()` results. Let's create an array to "store" them, and return a new Observable:

```ts
address$ = users$.pipe(
  switchMap((users: User[]) => {
    // Create and initialize the array
    const addressArray$: Observable<Address>[] = [];
    // Iterate over 'users' array
    users.forEach(user => {
      const address$: Observable<Address> = getAddress(user.id);
      addressArray$.push(address$);
    });
    // [Observable<Address>, Observable<Address>, ....., Observable<Address>]
    return forkJoin(addressArray$);
  })
);
```

The following operations are performed inside `switchMap` call:

* An empty array is created in order to "contain" the results of every `getAddress` call. This array will expect to contain a set of `Observable<Address>`.
* **users.forEach** iterates over the `users` array to add every `Observable<Address>` to the previous `addressArray$` variable.
* **forkJoin(addressArray$)**. We have an array of _Observables_ at this point and the `forkJoin` operator will do the following:
  * Accept the array of Observables as an input(`Observable<Address>[]`)
  * It will wait for every Observable(from the array) to complete and then combine the last values they emitted.
  * Finally, it will return an array of `Address[]` as a new Observable: `Observable<Address[]>`

As a final step, you'll need to _subscribe_ as follows.

```ts
// Subscription
address$.subscribe((address: Address[]) => {
  console.log({ address }); // Prints the array of addresses: Address[]
});
```

The result would be the following JSON object:

```json
{
  "address": [
  {
    "street": "2180 BELLFLOWER",
    "country": "USA",
    "state": "AL",
    "city": "Madison",
    "zipCode": 35064
  },
  {
    "street": "845 ODOM ROAD, SUITE 200",
    "country": "USA",
    "state": "CA",
    "city": "Los Angeles",
    "zipCode": 90720
  },
  {
    "street": "9025 QUEENS BLVD",
    "country": "USA",
    "state": "NY",
    "city": "Queens",
    "zipCode": 11355
  }
]
}
```

### Demo Project

Find the complete project running in [StackBlitz](https://stackblitz.com/edit/rxjs-switchmap-forkjoin-typescript?file=index.ts). Don't forget to open the browser's console to see the results.

You can follow me on [Twitter](https://twitter.com/luixaviles) and [GitHub](https://github.com/luixaviles) to see more about my work.

---
*This Dot Labs is a modern web consultancy focused on helping companies realize their digital transformation efforts. For expert architectural guidance, training, or consulting in React, Angular, Vue, Web Components, GraphQL, Node, Bazel, or Polymer, visit [thisdotlabs.com](https://www.thisdotlabs.com).*

*This Dot Media is focused on creating an inclusive and educational web for all.  We keep you up to date with advancements in the modern web through events, podcasts, and free content. To learn, visit [thisdot.co](https://www.thisdot.co).*