how can i run one script for multiple times and store value then send it to frontend using google-search-results-nodejs client

Viewed 139

Let's suppose i have 10 keywords kewords1, keyword2. i want to run this script for all keywords with mern stack and then send this response to frontend react . please let me know how i can do that?

Here's simple code.

const SerpApi = require('google-search-results-nodejs')
const search = new SerpApi.GoogleSearch("Your Private Key")
search.json({
 q: "Coffee", 
 location: "Austin, TX"
}, (result) => {
  console.log(result)
})
1 Answers

I don't know which part of the app you're asking to help to implement. Below are the backend and frontend parts.

Backend

Try it on Replit.

const express = require("express");
const { GoogleSearch } = require("google-search-results-nodejs");
const search = new GoogleSearch(process.env.API_KEY);

const app = express();

app.set('json spaces', 2)
app.use(express.json());

// Set routes
app.get("/", (req, res) => {
  res.send("Make a request to /search?q=coffee&q=cake");
});

app.get("/search", (req, res) => {
  // The number of queries should be limited in a real application
  // but it's ommitted here in favor of simplicity
  const queries = Array.from(req.query.q);

  makeSearches(queries).then((results) => {
    res.send(results);
  });
});

// API

// Workaround to make it work with Promises
// https://github.com/serpapi/google-search-results-nodejs/issues/4
function promisifiedGetJson(params) {
  return new Promise((resolve, reject) => {
    try {
      search.json(params, resolve);
    } catch (e) {
      reject(e);
    }
  });
}

function makeSearches(queries) {
  const promises = queries.map((q) => {
    const params = {
      q,
      location: "Austin, TX",
    };

    return promisifiedGetJson(params);
  });

  return Promise.all(promises);
};

// Start server

app.listen(3000, () => {
  console.log(`Server is running on port: 3000`);
});

Sample request and response:

$ curl -s 'https://mern-serpapi-nodejs.serpapi.repl.co/search?q=stackoverflow&q=github&q=deno' | jq -r '.[].search_metadata'
{
  "id": "61fad426607393485726c241",
  "status": "Success",
  "json_endpoint": "https://serpapi.com/searches/582d9fcdfd66a739/61fad426607393485726c241.json",
  "created_at": "2022-02-02 18:57:42 UTC",
  "processed_at": "2022-02-02 18:57:42 UTC",
  "google_url": "https://www.google.com/search?q=stackoverflow&oq=stackoverflow&uule=w+CAIQICIdQXVzdGluLFRYLFRleGFzLFVuaXRlZCBTdGF0ZXM&sourceid=chrome&ie=UTF-8",
  "raw_html_file": "https://serpapi.com/searches/582d9fcdfd66a739/61fad426607393485726c241.html",
  "total_time_taken": 0.66
}
{
  "id": "61fad42617f923aa08188dfc",
  "status": "Success",
  "json_endpoint": "https://serpapi.com/searches/667255ede060a7ae/61fad42617f923aa08188dfc.json",
  "created_at": "2022-02-02 18:57:42 UTC",
  "processed_at": "2022-02-02 18:57:42 UTC",
  "google_url": "https://www.google.com/search?q=github&oq=github&uule=w+CAIQICIdQXVzdGluLFRYLFRleGFzLFVuaXRlZCBTdGF0ZXM&sourceid=chrome&ie=UTF-8",
  "raw_html_file": "https://serpapi.com/searches/667255ede060a7ae/61fad42617f923aa08188dfc.html",
  "total_time_taken": 0.96
}
{
  "id": "61fad4261baebbb454181a3a",
  "status": "Success",
  "json_endpoint": "https://serpapi.com/searches/ae665860b250fd5f/61fad4261baebbb454181a3a.json",
  "created_at": "2022-02-02 18:57:42 UTC",
  "processed_at": "2022-02-02 18:57:42 UTC",
  "google_url": "https://www.google.com/search?q=deno&oq=deno&uule=w+CAIQICIdQXVzdGluLFRYLFRleGFzLFVuaXRlZCBTdGF0ZXM&sourceid=chrome&ie=UTF-8",
  "raw_html_file": "https://serpapi.com/searches/ae665860b250fd5f/61fad4261baebbb454181a3a.html",
  "total_time_taken": 0.61
}

Explanation

The answer consists of two parts: make N requests using SerpApi and return the response.

Make N requests

At the moment, google-search-results-nodejs doesn't have a Promise API so the workaround to make it work with Promises is required. With callback API it's also possible but it requires the usage of Array#reduce.

function promisifiedGetJson(params) {
  return new Promise((resolve, reject) => {
    try {
      search.json(params, resolve);
    } catch (e) {
      reject(e);
    }
  });
}

With the Promise API, a list of keywords will be mapped into an array of Promises.

function makeSearches(queries) {
  const promises = queries.map((q) => {
    const params = {
      q,
      location: "Austin, TX",
    };

    return promisifiedGetJson(params);
  });

  return Promise.all(promises);
};

API action (route) to return the response

It gets an array of query parameters and calls a makeSearches function.

app.get("/search", (req, res) => {
  // The number of queries should be limited in a real application
  // but it's ommitted here in favor of simplicity
  const queries = Array.from(req.query.q);

  makeSearches(queries).then((results) => {
    res.send(results);
  });
});

Front-end part

Here are two full-stack React projects that use an API from above.

Demo

https://user-images.githubusercontent.com/282605/155405202-50bceacb-57f6-4729-b950-ea6e5ef6c295.mp4

Both projects are deployed to Vercel. Next.js app is simpler because that's the value proposition of Next.js.

Express.js app has SSR in development mode only. It uses Vite to render React components on the server. Next.js app has SSR in dev and prod.

React component itself is the same for both cases:

// App.jsx

import React from "react";
import ReactDOM from "react-dom";

import React, { useState } from "react";
import { SearchResults } from "./SearchResults";

export function App() {
  const [q, setQ] = useState("coffee, cats");
  const [searchResults, setSearchResults] = useState([]);
  const [isLoading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  function handleFormSubmit(event) {
    event.preventDefault();

    const params = new URLSearchParams({
      q,
    });

    setLoading(true);
    fetch(`/api/search?${params}`)
      .then((res) => res.json())
      .then(
        (results) => {
          setSearchResults(results);
          setLoading(false);
          setError(null);
        },
        (error) => {
          setError(error);
        }
      );
  }

  function handleQChange(event) {
    event.preventDefault();
    setQ(event.target.value);
  }

  return (
    <>
      <h1>SerpApi example in MERN stack</h1>

      <form action="/api/search" method="get" onSubmit={handleFormSubmit}>
        <label>
          <label>Queries (separated by comma):</label>&nbsp;
          <input name="q" value={q} placeholder={q} onChange={handleQChange} />
          <br />
          <input
            type="submit"
            value={isLoading ? "Loading..." : "Search"}
            disabled={isLoading} />
        </label>
      </form>

      <br />

      <SearchResults
        results={searchResults}
        isLoading={isLoading}
        error={error} />
    </>
  );
}

ReactDOM.hydrate(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root")
);

SearchResults.jsx

function SearchResults({ results, isLoading, error }) {
  if (isLoading)
    return (
      <p>
        Loading...
      </p>
    );

  if (!results || results.length === 0) {
    return <p>Click &quot;Search&quot; &uarr; to continue.</p>;
  }

  if (error) return <p>Error: {error}</p>;

  return (
    <section>
      <h3>Search results ({results.length})</h3>
      <ul>{results.map(r => (<li key={r.search_metadata.id}>{r.search_metadata.id}</li>))}</ul>
    </section>
  );
}

Disclaimer: I work at SerpApi.


This question was asked in the Github repository of google-search-results-nodejs one day before it appeared here.

Related