30

I need to make a function to make HTTP calls sequentially inorder to use response of one call into other one like getting IP address of user from first call and use that IP to register user in second call.

Demo code:

registerUser(user: User) {
    this.utility.getIpAddress()
    .subscribe(data => {
        this.ipAddress = data.ip;
    });
    const body = {
        UserName: user.UserName,
        Email: user.Email,
        //...
        UserIP: this.ipAddress,
    }
    return this.http.post(this.registerAPI, body);
}
Abdul Rafay
  • 3,241
  • 4
  • 25
  • 50
  • 9
    Possible duplicate of [How to chain Http calls in Angular2](https://stackoverflow.com/questions/34104638/how-to-chain-http-calls-in-angular2) – Heretic Monkey Jul 06 '18 at 14:24
  • 1
    @HereticMonkey I can't seem to understand the solution from that question. My case is a little different, as I needed to make first html call wait for it to complete, use the data from response and then make the next http call and return the observable. SwitchMap seems to work for me rather than MergeMap opposed to that question's solution as SwitchMap does not send in parallel, it map to observable, complete previous inner observable, emit values. – Abdul Rafay Jul 06 '18 at 15:55

1 Answers1

27

This can be achieved using the switchMap operator. This example uses RxJS 5.5+ pipeable operators.

import { switchMap } from 'rxjs/operators';

registerUser(user: User) {
  return this.utility.getIpAddress().pipe(
    switchMap(data => {
      this.ipAddress = data.ip;

      const body = {
        UserName: user.UserName,
        Email: user.Email,
        UserIP: this.ipAddress,
      };

      return this.http.post(this.registerAPI, body);
    })
  )
}

RxJS < 5.5:

import { switchMap } from 'rxjs/operators';

registerUser(user: User) {
  return this.utility.getIpAddress()
    .switchMap(data => {
      this.ipAddress = data.ip;

      const body = {
        UserName: user.UserName,
        Email: user.Email,
        UserIP: this.ipAddress,
      };

      return this.http.post(this.registerAPI, body);
    });
}
Kostas Nitaf
  • 428
  • 1
  • 2
  • 12
Alexander Staroselsky
  • 37,209
  • 15
  • 79
  • 91
  • 1
    `switchMap` does not send in parallel, it "Map to observable, complete previous inner observable, emit values". It waits for inner `getIpAddress()` to complete before executing body. – Alexander Staroselsky Jul 06 '18 at 14:46
  • Sorry, I didn't see the question properly, also deleted my comment.. :D – Ashish Ranjan Jul 06 '18 at 14:46
  • @AlexanderStaroselsky I'm getting this error while trying to subscribe to returned observable from "registerUser()": this.userService.registerUser(form.value) .subscribe((data) => console.log(data)); error TS2339: Property 'subscribe' does not exist on type 'void' – Abdul Rafay Jul 06 '18 at 14:55
  • My answer depends on your version of RxJS. It's line with current Angular [documentation](https://angular.io/guide/rx-library). If you are using an older version of RxJS, this may not work for you. I can provide an example using an older version. – Alexander Staroselsky Jul 06 '18 at 14:57
  • 2
    @AlexanderStaroselsky: Pardon my naiveness, but woudn't a `return` with `this.utility.getIpAddress()` sove the issue? – Ashish Ranjan Jul 06 '18 at 15:00
  • Yes, you are right, good catch. I wasn't seeing how he was consuming `registerUser` exactly. I updated the answers to add the `return` statements. – Alexander Staroselsky Jul 06 '18 at 15:01
  • AlexanderStaroselsky this is perfect. @AshishRanjan yup return statement was the issue. Thanks alot to both of you! – Abdul Rafay Jul 06 '18 at 15:07
  • Hi @AlexanderStaroselsky in case I want to make 4-5 calls sequentially in order then how the strcuture would look like , thanks – Enthu Aug 07 '19 at 18:44
  • @Enthu At a basic level you can just add more `switchMap()` statements. It would be important that you'd need to return an Observable each time. Otherwise, if each statement does not depend on previous statements, operators such as `forkJoin` can be a great option for running multiple calls together. – Alexander Staroselsky Aug 07 '19 at 18:47
  • I had used switchmap : eg: this.siteService.getFloorRooms(this.floorRef) .pipe(switchMap(res => { const rest = res let elementId = []; res.rows.forEach( (element) => { elementId.push(element.id) }); for (const color of elementId){ this.roomref = (color).split(":")[1].split(" ")[0]; } return this.siteService.checkPairedRooms(this.roomref) })). subscribe( m => { this.rooms = m.rows; if(m.rows.length == 1){ this.paired = true; } } ); – Enthu Aug 07 '19 at 18:58
  • @AlexanderStaroselsky, Now after checkedPairedRooms I want to make one or two more calls then how should i structure it, thanks – Enthu Aug 07 '19 at 18:59
  • You would probably want to create a new question for your specific issue to get the best support. At a basic level you will not be able to return from within a forEach() and will likely need to take advantage of operators such as mergeMap, forkJoin, or similar to make the multiple calls and return the result in a specific way. – Alexander Staroselsky Aug 07 '19 at 18:59
  • Why use pipe() when there is only 1 operator? – user1034912 Feb 09 '22 at 21:33
  • @user1034912 because this example executes switchMap which returns another observable using the emitted observable value from `this.utility.getIpAddress()`. Their question was to "chain" HTTP requests together, each of which return observables. You can't and don't really want to try making another HTTP request or similar observable operation in `subscribe()` if that's what you mean. – Alexander Staroselsky Feb 09 '22 at 23:55