[best practice] Chained API requests

Hi,

I’m currently developing an application and I now take care of the API part.
And I wonder what is the best method, the best practice to make API calls.

Indeed, I have the following data/table in my DB:

So I need to, in this order:

  1. Request the User class, save “name” in “myPrintedData” (the one I will display on the app)
  2. Take the adress_id1 and make the findbyID request, then add the property to “myPrintedData
  3. Take phone_id and make a findById request, then add the property to “MyPrintedObject
  4. Then return “MyPrintedData

I found the chained API calls really messy, is it really the best way to achieve this kind of data request ?

  • Do I need to create an API endpoint that perform this job ?
  • Do I need to modify the db tables ?
  • Or is it ok ? :slight_smile:

My code looks like this (don’t throw up…)

getPosts(): Observable<any> {
    this.http.get<any[]>(this.baseURL + 'posts').subscribe( (posts) => {
      posts.forEach((post, index) => {
        const data = {description: post.description, nb_like: post.nb_like, created_at: post.created_at, comments_id: post.comments_id};
        this.loadedPosts.push(data);

        this.http.get<any>(this.baseURL + 'tipseurs/' + post.author_id).subscribe( (author => {
          this.loadedPosts[index].author = author.pseudo;

          this.http.get<any>(this.baseURL + 'photos/' + post.photo_id).subscribe( (photo) => {
            this.loadedPosts[index].imagePath = photo.content;

            this.http.get<any>(this.baseURL + 'institutes/' + post.institute_id).subscribe( (institute) => {
              this.loadedPosts[index].institute = institute.name;


            });
          });
        }));
      });
    });
    return of(this.loadedPosts);
  }

And it’s working :rofl:
For information, I used Loopback API and Angular HttpClientModule.

  • By the way, is it better to use the Http Ionic module ?

Thanks

Any time I have nested futures, I’m uncomfortable.

“Need”? No, but that’s definitely where I would do this task. The rest of this post, however, assumes that we’re still doing everything client-side.

That depends on who can share what. If everything is 1-to-1, meaning that each user has exactly one address, one phone number, and no users can share any of those things, then I don’t see the point in having them in separate tables. We’re still going to leave that alone, and address how to do this most cleanly in RxJS.

Some heuristics I use here:

  • no any
  • everything is functional, no external state (this means that inside getPosts, this is read-only)
  • no nested subscriptions
  • no subscribing in the place we generate the future (meaning no subscribe in our service at all)

The basic technique for flattening your structure is outlined in this post.

Thanks for a lot for your answers.
But I’m sorry I don’t understand everything :grin: :

That’s what I was thinking I will try to do it this way.

In my example is quiet simplified but I think you’r right I can reduce the number of relation between tables.

Then :

  • no any
    • I agree but I didn’t implement the class yet and I still make some changes on my tables/classes.
  • everything is functional, no external state (this means that inside getPosts, this is read-only)
    • :astonished: that’s difficult to implement, so if I understand, in my example, I return everything and it’s only in my component that I create the “myPrintedObject” ?
  • no nested subscriptions
    • That’s exactly what’s disgust me, but I need to wait for the first response in order to execute the second request. This is the only way I found to achieve that. I saw your example and I already try something like that but I can’t manage to have two mergeMap (in the case you have 3 API calls to make).
  • no subscribing in the place we generate the future (meaning no subscribe in our service at all)
    • :astonished: WHAT !? This time I’m completely lost … How is it possible ? Again, even in your example (post) every functions return an Observable.
    • Eratum !!! You said No subscribe and not no Observable , my bad. I get it, but again it’s to be sure that I received the answer before executing the next request.

Thanks for your help again !!!

In that case, I would urge you to rename the columns away from snake_case and into camelCase (or do this in the REST middleware app, which is what I do).

No, that gets generated in the service. What I’m asking you to get away from is the imperative style of this.loadedPosts.push. Forcing yourself to write getPosts in a strictly functional style will make use of RxJS operators much more natural.

forkJoin is our friend here.

interface PostStub {
  id: string;
  authorId: string;
  photoId: string;
  instituteId: string;
}
interface Post {
  id: string;
  author: Author;
  photo: Photo;
  institute: Institute;
}
fetchPostStub(id: string): Observable<PostStub> {...}
fetchAuthor(id: string): Observable<Author> {...}
fetchPhoto(id: string): Observable<Photo> {...}
fetchInstitute(id: string): Observable<Institute> {...}
fleshOutPost(stub: PostStub): Observable<Post> {
  return forkJoin([this.fetchAuthor(stub.authorId), 
                   this.fetchPhoto(stub.photoId), 
                   this.fetchInstitute(stub.instituteId)]).pipe(
    map(([author, photo, institute]) => {
      return { id: stub.id, author, photo, institute };
  });
}
fetchPost(id: string): Observable<Post> {
  return this.fetchPostStub(id).pipe(
    mergeMap(stub => this.fleshOutPost(stub));
}

I think of producers and consumers. Consumers (components, usually) are the places where subscribe should happen. Producers (the service) returns the Observable, but does not ever subscribe to it. What you are doing with subscribe can be done with mergeMap.

1 Like

:heart_eyes:
Love what I see !

As you can imagine I have some difficulties with RxJS operators and I realize now that it can’t be. I need to work on that.
Ok I think I can manage to implement your code I understand the idea.

But now, you make me hesitate, do I modify the API to deliver directly the necessary values or I make the forkJoin calls.

I think, I will first try to modify the API and then I will try to follow your best practices for the Ionic part.

Will do right now, besides I knew it :man_facepalming:

Thanks a LOT !
I will keep you up to date when I fixed the issue.