Skip to main content

Command Palette

Search for a command to run...

Optimize API calls via Axios' CancelToken

Updated
โ€ข4 min read
Optimize API calls via Axios' CancelToken

Every product has its search bar, and there are two ways in which you can implement it. In the first use case scenario, you let the people type what they want to search and ask them to click submit/search button at the end.

Secondly, Like google suggestion, hashnode search bar, you show the possible results/suggestion based on every character enter by a user. Of course, it will add extra load onto the server, but you will provide a good user experience.

SO, Is there is any way in which we can reduce the load onto the server? Yes, By using Axios' CancelToken, if a user sends a new request before the previous one gets fulfilled, an older one will get cancelled and removed from the pipeline and a new request will be executed, without giving extra load onto a server

How? Don't worry, I am here to explain you.

Step #0 Setting up a React Project โš™๏ธ

We will create a simple React application using create-react-app or you can use any online editor like Stackblitz, Codesandbox etc. After creating react app, also install bootstrap, reactstrap, Axios package using command

npm install bootstrap reactstrap axios

Step #1 Create simple Input-Search bar ๐Ÿ”Ž and User Card

Now, create a basic input search bar inside SearchBar.js (React functional component), saving the input value in a state

import React, { Fragment, useState } from 'react'
import 'bootstrap/dist/css/bootstrap.min.css'
import './App.css';
import SearchBar from './Component/SearchBar';

const App = () => {
const [username,setUser] = useState("mayur-keswani")
return (
  <Fragment>
   <SearchBar username={username} changed={(event)=>setUser(event.target.value)}/>
  </Fragment>
);
}
import React, { Fragment } from 'react'
import './SearchBar.css'
const SearchBar = (props) =>{
return(
<div className="form-group mx-auto mt-5 search-tab" style={{width:"80%"}}>
 <input type="text"
    className="form-control"
    placeholder="Enter username"
    value={props.username}
    onChange={props.changed}
 />
</div>
)
}
export default SearchBar

Now pass the input value entered by the user as a prop to the UserDetails component. And display the value using the card utility component of reactstrap

import React, { Fragment, useEffect, useState } from 'react'
import {
 Card,
 CardText,
 CardBody,
 CardLink,
 CardTitle,
 CardSubtitle } from 'reactstrap';

const UserInfo = ({username}) =>{
 const [userDetails,setUserDetails] = useState({});
 return(
   <Card className="mx-auto bg-secondary" style={{width:"50%"}}>
    <CardBody>
     <CardTitle tag="h5">{username}</CardTitle>
     <CardSubtitle tag="h6">(Searched User)</CardSubtitle>
    </CardBody>
  </Card>
 )
}
export default UserInfo;

Animation1.gif Pretty Simple, Right?

Step #2 Getting details of searched User ๐Ÿ‘ฅ

We will be using Github API to fetch publicly available information of a user. Click the following link,

https://api.github.com/users/mayur-keswani

This is an API endpoint that GitHub has provided that return information for a specified user in JSON format. You can dynamically pass in the GitHub username that we'd like to request info for.

If your JSON isn't nicely formatted, I highly recommend you download the free JSONView Chrome Extension.

Let's take the username props that we're passing into our function and use that dynamically within the GitHub API endpoint.

`https://api.github.com/users/${username}`
const UserInfo = ({username}) =>{
  const [userDetails,setUserDetails] = useState({});
  const fetchUser = async()=>{ 
    axios.get(`https://api.github.com/users/${username}`)
      .then(res=>{
          setUserDetails(res.data)  
      })
      .catch(err=>{
        setUserDetails(null)
        console.log(err.message)
      })
  }

  useEffect(()=>{
    fetchUser()
  },[username])

  return(
    <Card className="mx-2 bg-light" >
     {
       userDetails?
        (
        <>
        <CardBody>
            <CardTitle tag="h3" className="mx-1">{userDetails.name}</CardTitle>
            <CardSubtitle tag="h6" className="mx-2 text-muted">Type :{userDetails.type}</CardSubtitle>
        </CardBody>
        <img className="img-thumbnail mx-1" 
             width="200px" 
             height="50%" 
             src={userDetails.avatar_url} 
             alt="avatar" />
       <CardBody className="mx-1">
            <CardText>{userDetails.bio}</CardText>
            <CardText> Hireable :{userDetails.hireable?" YES":" NO"}</CardText>
            <CardText>Location : {userDetails.location}</CardText>
            <CardLink href={userDetails.repos_url}>Repos URL</CardLink>
       </CardBody>
        </>
        ):
        (
        <CardBody>
             <CardTitle tag="h5" className="text-danger">Not Found</CardTitle>
             <CardSubtitle tag="h6">(Searched User)</CardSubtitle>
         </CardBody>
        )
     }                
     </Card>

  )
}

After running the code above, you will get a response with status code 200 (successful) and an object containing information about a searched user. Store this information on state and render it using the Card utility of reactstrap. From here you can certainly put your own spin on things and truly make them your own.

In case, if there is no such user with this name, you will get a response with status code 404 (Not Found) and an object containing an error message.

Animation2.gif

Step #3 Cancelling previous request

With every new character entered by the user, a request is being sent to GitHub API. By the time it gets fulfilled, a new request is being made.

In order to cancel the previous request, we need to pass the cancel token as a config with every Axios request and call the cancel() function whenever we need to cancel the previous Axios request.

You can create a cancel token using the CancelToken.source factory as shown below:

useEffect(()=>{
    const CancelToken=axios.CancelToken.source()
    fetchUser(CancelToken)
    return ()=>{
      CancelToken.cancel(`Previous Request Cancelled @${username}`)
    }
  },[username])
    const fetchUser = async(CancelToken)=>{
      axios.get(`https://api.github.com/users/${username}`,{
              cancelToken:CancelToken.token
       })
      .then(res=>{
          setUserDetails(res.data)  
      })
      .catch(err=>{
        setUserDetails(null)
        console.log(err.message)
      })

  }

We can use the same cancel token for multiple Axios requests.

Animation5.gif Awesome! ๐Ÿ‘๐Ÿ‘

Surely. you can use a debouncing or custom function that waits for some time (maybe some hundred milliseconds) before firing the API call and making the request only after the said amount of time interval from the last on-change event. In this way, we reduce the network conjunction and reduce the number of requests to the backend.

But, the Use case here is to cancel the previous request.

Source Code : Repo Link

Reference

๐Ÿ”— GITHUB API Documentation : https://docs.github.com/en/rest/overview/resources-in-the-rest-api

๐Ÿ”— Axios : https://github.com/axios/axios#cancellation


I keep writing about the things I learned and applied. So you can connect with me on Twitter, Github. Also, subscribe to my newsletter and stay up-to-date with my latest blog posts.

Keep Learning!โšก