HTTP calls in Angular where one output depends on another
When making HTTP calls in Angular where one output depends on another, you can use RxJS operators like switchMap, mergeMap, or concatMap to handle dependent HTTP requests. Here's how you can achieve this:
1. Using switchMap for Dependent HTTP Calls
switchMap is used when the second HTTP call depends on the result of the first one. It cancels the previous request if a new one is triggered.
Example:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class DataService {
private apiUrl1 = 'https://api.example.com/resource1';
private apiUrl2 = 'https://api.example.com/resource2';
constructor(private http: HttpClient) {}
getResource1(): Observable<any> {
return this.http.get(this.apiUrl1);
}
getResource2(dependentId: string): Observable<any> {
return this.http.get(`${this.apiUrl2}/${dependentId}`);
}
}
Component Code:
import { Component, OnInit } from '@angular/core';
import { DataService } from '../../services/data.service';
import { switchMap } from 'rxjs/operators';
@Component({
selector: 'app-my-component',
template: `
<div *ngIf="data">{{ data | json }}</div>
`,
})
export class MyComponent implements OnInit {
data: any;
constructor(private dataService: DataService) {}
ngOnInit(): void {
this.dataService.getResource1()
.pipe(
switchMap((response1: any) => {
const dependentId = response1.id; // Extract dependent ID from the first response
return this.dataService.getResource2(dependentId); // Use it in the second call
})
)
.subscribe({
next: (response2) => {
this.data = response2; // Handle the final response
},
error: (error) => {
console.error('Error:', error);
},
});
}
}
2. Using concatMap for Sequential HTTP Calls
concatMap ensures that the second HTTP call starts only after the first one completes. It processes requests sequentially.
Example:
this.dataService.getResource1()
.pipe(
concatMap((response1: any) => {
const dependentId = response1.id;
return this.dataService.getResource2(dependentId);
})
)
.subscribe({
next: (response2) => {
this.data = response2;
},
error: (error) => {
console.error('Error:', error);
},
});
3. Using mergeMap for Parallel Execution
mergeMap is used when you want to execute multiple HTTP calls in parallel but still depend on the result of the first call.
Example:
this.dataService.getResource1()
.pipe(
mergeMap((response1: any) => {
const dependentId = response1.id;
return this.dataService.getResource2(dependentId);
})
)
.subscribe({
next: (response2) => {
this.data = response2;
},
error: (error) => {
console.error('Error:', error);
},
});
4. Handling Multiple Dependent Calls
If you have multiple dependent HTTP calls, you can chain them using switchMap or other operators.
Example:
this.dataService.getResource1()
.pipe(
switchMap((response1: any) => {
const dependentId1 = response1.id;
return this.dataService.getResource2(dependentId1);
}),
switchMap((response2: any) => {
const dependentId2 = response2.id;
return this.dataService.getResource3(dependentId2); // Third dependent call
})
)
.subscribe({
next: (response3) => {
this.data = response3;
},
error: (error) => {
console.error('Error:', error);
},
});
5. Error Handling
Use the catchError operator to handle errors gracefully.
Example:
import { catchError } from 'rxjs/operators';
import { of } from 'rxjs';
this.dataService.getResource1()
.pipe(
switchMap((response1: any) => {
const dependentId = response1.id;
return this.dataService.getResource2(dependentId);
}),
catchError((error) => {
console.error('Error occurred:', error);
return of(null); // Return a fallback value or empty observable
})
)
.subscribe({
next: (response2) => {
this.data = response2;
},
error: (error) => {
console.error('Error:', error);
},
});
6. Scenario
To handle 4 HTTP calls in Angular where all calls are dependent on each other, you can use mergeMap from RxJS. Here's how you can chain these calls:
- You have 4 HTTP calls:
getData1()→ First API call.getData2()→ Depends on the result ofgetData1().getData3()→ Depends on the result ofgetData2().getData4()→ Depends on the result ofgetData3().
Service Implementation
Example Service:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class DataService {
private apiUrl1 = 'https://api.example.com/data1';
private apiUrl2 = 'https://api.example.com/data2';
private apiUrl3 = 'https://api.example.com/data3';
private apiUrl4 = 'https://api.example.com/data4';
constructor(private http: HttpClient) {}
getData1(): Observable<any> {
return this.http.get(this.apiUrl1);
}
getData2(param: string): Observable<any> {
return this.http.get(`${this.apiUrl2}?id=${param}`);
}
getData3(param: string): Observable<any> {
return this.http.get(`${this.apiUrl3}?id=${param}`);
}
getData4(param: string): Observable<any> {
return this.http.get(`${this.apiUrl4}?id=${param}`);
}
}
Using mergeMap to Chain 4 HTTP Calls
Component Code:
import { Component, OnInit } from '@angular/core';
import { DataService } from '../../services/data.service';
import { mergeMap } from 'rxjs/operators';
@Component({
selector: 'app-my-component',
template: `
<div *ngIf="finalData">{{ finalData | json }}</div>
`,
})
export class MyComponent implements OnInit {
finalData: any;
constructor(private dataService: DataService) {}
ngOnInit(): void {
this.dataService.getData1()
.pipe(
mergeMap((response1: any) => {
const param1 = response1.id;
return this.dataService.getData2(param1);
}),
mergeMap((response2: any) => {
const param2 = response2.id;
return this.dataService.getData3(param2);
}),
mergeMap((response3: any) => {
const param3 = response3.id;
return this.dataService.getData4(param3);
}),
catchError((error) => {
console.error('Error occurred:', error);
return of(null); // Return a fallback value or empty observable
})
)
.subscribe({
next: (response4) => {
this.finalData = response4;
},
error: (error) => {
console.error('Error:', error);
},
});
}
}
7. Summary
- Use
switchMapwhen the second HTTP call depends on the result of the first and you want to cancel previous requests if a new one is triggered. - Use
concatMapfor sequential execution of dependent HTTP calls. - Use
mergeMapfor parallel execution of HTTP calls. - Use
catchErrorto handle errors gracefully.
This approach ensures clean and maintainable code for handling dependent HTTP requests in Angular.