Sending a POST request to different APIs in Angular using RxJS
Tags: angular, javascript, typescript, rxjs
Published: 2021-04-06 20:28:19

### Topic
RxJs is a powerful tool to play with streams, make operations on them. This demo project is an example of sending one form value to two different APIs (with a toggle button deciding which one should be used).


### Setup project
I created an angular project by typing (answered CLI question by default):
```
ng new store-data-example
```
I love using Angular Material, so in the root directory, I typed (default answers):
```
ng add @angular/material 
```
Now my project is ready to code.

### Service
To be able to make HTTP calls I added `HttpClientModule` to imports inside `AppModule` file (from `@angular/common/http`). 
I made a service to make HTTP calls to two endpoints separatly.
The first one is firing to [Httpbin](httpbin.org) endpoint.
The second one is firing to [JSONPlaceholder](https://jsonplaceholder.typicode.com/).
Here is full code of service:
```typescript
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root',
})
export class EndpointService {
  constructor(private http: HttpClient) {}

  sendBin(data: any): Observable<boolean> {
    console.info('HTTP BIN sent');
    return this.http.post('https://httpbin.org/post', { data }).pipe(
      map(_ => true),
      catchError(error => of(false))
    );
  }

  sendJsonPlaceholder(data: any): Observable<boolean> {
    console.info('JSON Placeholder sent');
    return this.http.post('https://jsonplaceholder.typicode.com/posts', { data }).pipe(
      map(_ => true),
      catchError(error => of(false))
    );
  }
}


```
I simplified it, and all success I am treating as positives and all errors as negatives.
* [map](https://rxjs.dev/api/operators/map) operator converts one value to another
* [catchError](https://rxjs.dev/api/operators/catchError) operator is listening for any errors on stream
Of course, I could use the `error` object inside `catchError` to check response status.
Both methods return observable with the boolean results depending on the HTTP response.

### The form
To be able to use some Angular Material elements and angular forms I imported some modules into `AppModule`:
```typescript
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { HttpClientModule } from '@angular/common/http';
import { MatButtonModule } from '@angular/material/button';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    HttpClientModule,
    MatButtonModule,
    ReactiveFormsModule,
    MatFormFieldModule,
    MatInputModule,
    MatSnackBarModule,
    FormsModule,
    MatSlideToggleModule,
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}
```
My form is really simple, with two fields:
```typescript
  form = this.formBuilder.group({
    firstName: [null],
    lastName: [null],
  });
```
In constructor, I added three classes:
```typescript
    private endpointService: EndpointService,
    private formBuilder: FormBuilder,
    private snackBar: MatSnackBar
```
* `endpointService` my HTTP service
* `formBuilder` Angular reactive [form builder](https://angular.io/api/forms/FormBuilder)
* `snackBar` Angular Material [Snack Bar](https://material.angular.io/components/snack-bar/overview)

My component has also two other properties:
* `subject` RxJS [Subject](https://rxjs.dev/guide/subject) which allows me to pass data to the service 
* `enpointToggle` true for JSONPlaceholder, false for HttpBin

I am sending form value to the subject by using the `next` method:
```typescript
  onSubmit(): void {
    this.subject.next(this.form.value);
  }
```
Depending on the `enpointToggle` value I am sending data to one of the endpoints:
```typescript
    this.subject
      .asObservable()
      .pipe(
        switchMap(value => iif(
          () => this.enpointToggle,
          this.endpointService.sendJsonPlaceholder(value),
          this.endpointService.sendBin(value),
        ))
      )
      .subscribe(result => this.snackBar.open(result ? `Send to ${this.endpoint}` : 'Error', '', { duration: 3000 }))
```
* `asObservable` allows me to use the subject as observable and treat it as a stream
* `pipe` method is for working with data from the stream
* `switchMap` [operator](https://rxjs.dev/api/operators/switchMap) for switching from one observable (with form value) to another (HTTP call)
* `iif` [function](https://rxjs.dev/api/index/function/iif) takes three arguments (and returns observable):
  * first takes a function which result decides which observable should be subscribed
  * second takes observable which is subscribed when the first function returns true
  * third takes observable which is subscribed when the first function returns false
IMPORTANT! `iif` evaluates both expressions in any case, but service will fire HTTP call only in one of them (depending on boolean return)
* `subscribe` calls `open` method on `snackBar` to show notification

The HTML code is also simple:
```HTML
<form [formGroup]="form" (ngSubmit)="onSubmit()">

  <mat-form-field appearance="fill">
    <mat-label>First name</mat-label>
    <input matInput formControlName="firstName">
  </mat-form-field>

  <mat-form-field appearance="fill">
    <mat-label>Last name</mat-label>
    <input matInput formControlName="lastName">
  </mat-form-field>

  <button type="submit" mat-flat-button color="primary">Send</button>

</form>
<mat-slide-toggle [(ngModel)]="enpointToggle">HttpBin / JSONPlaceholder</mat-slide-toggle>
```

Link to [repo](https://github.com/TomWebwalker/store-data-example).