API client patterns every front-end developer need to know

Prasad Jayakumar
Vue.js Developers
Published in
4 min readMay 1, 2021

--

Photo by Olav Ahrens Røtne on Unsplash

REST API is the de facto standard for the Web Application (client) to communicate with Backend Application (server).

REST API popularity increased due to its simplicity compared to well-established SOAP based Web services. GraphQL, which has clear specification and good community support, is currently playing second fiddle. “Sooner” the king RPC will be back 😃

Web Applications will need one or more of the following API client patterns. I will be using Axios and JS Promise for the demo

  1. Basic asynchronous call
  2. Orchestration
  3. Split-Join with fail-fast / Split-Join with all-settled
  4. Dynamic Split-Join
  5. Controlled concurrency
  6. Rat race

For demo, let’s use the following fake APIs provided by dummyapi.io

Users List
https://dummyapi.io/data/api/user?limit=10
User Full Profile
https://dummyapi.io/data/api/user/{userId}
User Posts List
https://dummyapi.io/data/api/user/{userId}/post
  1. Basic asynchronous call

First pattern is straight-forward.

  • Make a API call
  • On successful response, perform the intended action
  • On error, gracefully handle the error scenario

2. Orchestration

API Orchestration is about making several different API calls in a sequence (sometimes in parallel)

  • to perform a set of actions or
  • to merge responses from various API calls for showing a unified view

3. Split-Join with fail-fast

Split-join is a pattern, where we make several API calls concurrently and wait for responses.

Axios has deprecated axios.all in favor of Promise.all

Fail-Fast

If we have to move immediately to the next step on failure of any one of the API calls, then use fail-fast.

All-Settled

There are use-cases where we have to wait till all API calls either are successful or failed. In such scenario use all-settled. This approach will give you absolute control.

Promise.all() will reject immediately upon any of the input promises rejecting. In comparison, the promise returned by Promise.allSettled() will wait for all input promises to complete, regardless of whether or not one rejects. Consequently, it will always return the final result of every promise and function from the input iterable.
Source: MDN

4. Dynamic Split-Join

There are use-cases, where you will get dynamic list of data and have to make enrichment calls or some actions for each entry. In such situations, use dynamic split-join

The first example code is naive. Look for the potential problem. In the next example, I have provided a solution which involves a correlation id.

Naive example

Example with correlation logic is provided below. The logic shared is just one-way to do the correlation. If API provider supports, you can use custom header to pass on the correlation id and receive the same in response.

5. Controlled concurrency

In dynamic split-join example, I have limited the number of users to just 2, on purpose. Now, if we happen to get 10 or 60 users and we run dynamic split-join for all users, then the API server will get flooded with requests. Typically in production or in any public web site, there would be an API gateway which does rate-limit check and throw error. Controlled concurrency would be handy in these situations.

For any controlled executions or resource contention scenario, the common solution would involve some kind of Queue-Worker pattern or batching.

💡 Controlled concurrency use case is an indicator to improve your backend API design either following sideloading or embedded approach. Typically these will be handled in backend-for-frontend (BFF) layer in case more such lapses are prevalent or if the granular APIs are from product or third-party provider.

6. Rat Race

There are use cases such as “Search API” or similar where we might fire API call based on events (like text entered). There is subtle race conditions. We are assuming, the response would come in sequence. But in reality, the second request could come earlier than the first request.

I have named this pattern as Rat Race, just for fun 😅

💡Debounce and throttle will help to reduce the number API request fired, but will not help in avoiding the race condition

Rat-race execution result

Solution to avoid this race condition is to cancel the previous request, before initiating the new one. Axios provides CancelToken for this purpose.

Axios cancellation will avoid rat race

💡 Axios cancellation approach only averts the race condition on response path. Server could have received all the requests and might have processed.

One other pattern is stream. Axios (via XHR) currently does not support streaming. Fetch API supports.

Stream Pattern
Axios Issue#479: Support stream response in browser

Conclusion

In many situations, a slight improvement in API design or adoption of“backend-for-frontend (BFF) layer” could avoid complication in API client call. In all other cases, it would be better for front-end developer to prepare themselves for handling complex scenarios. Hopefully, these patterns would help you 😇

--

--