Best practice to handle input change requests with Google Custom Search

Best practice to handle input change requests with Google Custom Search

ยท

0 min read

Photo by Bench Accounting on Unsplash

In many projects, we would have search input that returns a list (via any external APIs) of data related to the keyword you are entering into the input. so, what's your way to request continuous data from the same end-point?

Before going further, Let me introduce myself. I'm a javascript developer and learner. here, I'm sharing some shortcodes and useful functions that can let you make your code faster and neat. So, if you haven't read my previous episodes' articles please check it out here or else stay tuned till the end to learn something new ๐Ÿ˜‹ .

Well, let's see one normal example to google search anything on input change. And display it into list below. ( in many cases we list out those items below input ) Here's I've given HTML and JS code with the result.

index.html

In HTML, I've just defined one input to take value from the user and passed it searchOnGoogle function. And defined one unordered list element to list out results after a query from the server. Also with that, I've imported Axios Library to make Http requests from the browser ( read more about it from here ).

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <title>Best practice - search input</title>
</head>
<body>

    <input type="text" name="searchInput" placeholder="Google anything..." oninput="searchOnGoogle(this.value)">
    <ul id="result-list"></ul>

    <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
    <script src="./index.js"></script>
</body>
</html>

index.js

In Javascript, I've defined searchOnGoogle function to make requests on google search engine with the user's query. ( You read more about google search API form here ). Where, I've used Axios library to make https request via get method. And in response I've set all received items into our unordered result list element.

function searchOnGoogle(query) {

   const GoogleAPIKey = "YOUR_GOOGLE_API_KEY"
   const SearchEngine = "YOUR_SEARCH_ENGINE_KEY"

    const RequestURL = `https://www.googleapis.com/customsearch/v1?key=${GoogleAPIKey}&cx=${SearchEngine}`

    const ResultList = $("#result-list");
    ResultList.empty();
    // Make a request for a user with a given Query
    axios.get(`${RequestURL}&q=${query}`)
    .then(({ data = {} }) => {
        if(data.items){
            data.items.map(({ title }) => ResultList.append(`<li>${title}</li>`))
        }
    })
}

Result

Here, I've search a word with 9 characters from input and you can see network besides it which is triggering API calls on google search on each character input.

google-custom-search

In the above scenario, guess user/ourselves type input like this!! -- which will crash our normal server and exceed limit for free google search. ๐Ÿคทโ€โ™‚๏ธ

speedy_typing_computer

So, what's a better way to request data on input change?

index.js

Here is one solution, request cancellation ( abort controller for many other request handlers). With Axios, we can create cancel token and for individual request and pass it to it. So, whenever we want to cancel that request while it's in the pending state we need to just execute cancel token.

let cache = [];
const cancel = [];

function searchOnGoogle(query) {

   const GoogleAPIKey = "YOUR_GOOGLE_API_KEY"
   const SearchEngine = "YOUR_SEARCH_ENGINE_KEY"

    const RequestURL = `https://www.googleapis.com/customsearch/v1?key=${GoogleAPIKey}&cx=${SearchEngine}`

    // Check if current url is in cache and execute abort controller via respective cancel tolken 
    // Or else pushed into cache array for next requestes
        if (cache.indexOf(RequestURL) !== -1) {
            const controller = cancel.filter(i => i.url === RequestURL);
            controller.map(item => item.c());
        } else {
            cache.push(RequestURL);
        }

        const ResultList = $("#result-list");
        ResultList.empty();

        // Make a request for a user with a given Query 
        // Create and added cancel token to abort request anytime.
        const cancelToken = new axios.CancelToken(((c) => {
            cancel.push({ url: RequestURL, c });
        }));   
        axios.get(`${RequestURL}&q=${query}`, { cancelToken })
        .then(({ data = {} }) => {
            if(data.items){
                data.items.map(({ title }) => ResultList.append(`<li>${title}</li>`))
            }
        })
}

In our case, I've created to empty collection in global scope; where one is to store all request URLs ( as caching ) and another one is to store all cancel tokens with request URLs. And whenever, user change value in input function will first check if we have the same URL in a cache, find it's cancel controller from cancel collection and execute it ( in our case URL will be always same ) otherwise, just store it into a cache.

And before requesting results through Axios, I'm creating cancel token for upcoming requests and in callback stored it into our cancel collection for next request use. And passed cancelToken to it's option of get method as second argument. that's it! ( you can read more about in detail from here. )

Now, let's see a difference?

axios-request-call

Did you figure it out right? As you see in-network when I was entering 9 characters, it requests each time on google search with updated queries but in this case as soon as a new request being made my all previous pending request canceled and stoped from hitting server unwantedly. So, your server will never know about those previous 8 requests.

This was all about Axios request handler, but if you are using any other one - I'm sure it'll have an abort controller for the same. ( let me know in a comment if you find any difficulty for finding it )

I found it one of the better ways to handle search input requests. so, I thought to share it with you guys too. I hope you liked my explanation (if yes, hit like โค๏ธ button ) and if you found it informative then do follow from here because I'll learn and share every day.๐Ÿ˜‹