Product Comparison App (JS Demo Project)
Tags: dropshipping, serpapi, node, react
Published: 2023-02-04 14:43:18

<h2 id='intro'>Intro</h2>

The main idea for creating this [eBay and Walmart dropshipping product research demo app](https://app-comparison.netlify.app/) was to show a practical example of one of the many options for using SerpApi.

This application could be improved to be an actual dropshipping tool (similar to [ZikAnalytics](https://www.zikanalytics.com/) for example) to research products to sell, and ability to compare the prices of certain products on eBay and Walmart to calculate profit and other possible criteria.

The backend is written in Node.js, the frontend in React. To create this app I was use the next npm packages:

- [serpapi](https://www.npmjs.com/package/serpapi);
- [express](https://www.npmjs.com/package/express);
- [js-levenshtein](https://www.npmjs.com/package/js-levenshtein);
- [dotenv](https://www.npmjs.com/package/dotenv);
- [axios](https://www.npmjs.com/package/axios);
- [file-saver](https://www.npmjs.com/package/file-saver);
- [xlsx](https://www.npmjs.com/package/xlsx).

<h2 id='how_to_run'>How to run locally?</h2>

📌Note: If you don't want to run the app locally, you can skip this part.

You can download the application files to your computer and run locally:

- [Backend repository](https://github.com/Mykhailo-Zub/comparison_server);
- [Frontend repository](https://github.com/Mykhailo-Zub/comparison_app).

<h3 id='run_backend'>Run backend</h3>

To run the backend you need to create `.env` file in the backend directory and write your API_KEY in it:

```
API_KEY="paste here your api key from https://serpapi.com/manage-api-key"
```

Then run the terminal and enter (you need to be installed Node.js):

\*<span style="font-size: 15px;">If you don't have Node.js installed, you can [download it from nodejs.org](https://nodejs.org/en/) and follow the installation [documentation](https://nodejs.dev/learn/introduction-to-nodejs).</span>

```bash
npm i
```

And then:

```bash
npm start
```

Now your server is working. To check it go to `http://localhost:4004` in your browser. If you see "Cannot GET /" everything is OK.

<h3 id='run_frontend'>Run frontend</h3>

Let's start our frontend. First, you need to change the backend URL. To do this open `src/requests.js` and change `baseURL` to `http://localhost:4004`. Now your app will send requests to your local backend.

Next, run the terminal in the frontend directory and enter:

```bash
npm i
```

And then:

```bash
npm start
```

After the application starts you can go to `http://localhost:3000` in your browser and you may see first screen:

![image](https://user-images.githubusercontent.com/64033139/215338328-819ab875-7da0-4187-8d6a-2b9ef1c192fc.png)

<h2 id='how_to_use'>How to use?</h2>

First, you need to enter a search query with a product name, choose where you want to sell a product and name match accuracy (default is more or equal to 50%) and click the "search" button.

📌Note: because when searching, the [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance) is used to compare the names of products, the search query must be similar to the full name of the product, otherwise the search will not return matches.

![image](https://user-images.githubusercontent.com/64033139/215799871-a1d05068-f14a-40ad-af1f-7b3ef6a06458.png)

Then you need to choose the product you are interested in and click the "compare results" button:

![image](https://user-images.githubusercontent.com/64033139/215800854-7dedfdf8-704c-487e-909b-f9a807629695.png)

And choose a product with a suitable profit and save it in "xlsx" format if you want:

![image](https://user-images.githubusercontent.com/64033139/215799942-8735705b-94aa-429c-9863-1ad074913624.png)

<h2 id='how_it_works'>How it works?</h2>

Because the frontend job is just to show you the information, the main part of the work makes on the backend. So I will try to explain to you how it works.

I'll skip the part where we create the server with `express` and focus on the part where we parse and process the results. If you don't know how to do it, [here's a Hello world `express.js` example from official docs](https://expressjs.com/en/starter/hello-world.html).

First, when the server receives a request on route `"/store"`, it handles it with `getResultsToChoose` function:

```javascript
//index.js
app.post("/store", getResultsToChoose);
```

In this function we get scraped results depending on `targetStore`, filter it and send it back to the frontend in the response:

```javascript
//storeResultsHandler.js
export const getResultsToChoose = async (req, res) => {
  const { targetStore, query, accuracy } = req.body;
  let results;
  if (targetStore === "ebay") {
    const ebayResults = await getStoreResults(targetStore, query);
    results = ebayFiltering(query, accuracy, ebayResults);
  } else {
    const walmartResults = await getStoreResults(targetStore, query);
    results = walmartFiltering(query, accuracy, walmartResults);
  }
  res.status(200).json({ filteredResults: results });
};
```

`getStoreResults` is used to get the organic results from the store using [Walmart Search Engine Results API](https://serpapi.com/walmart-search-api) and [Ebay Search Engine Results API](https://serpapi.com/ebay-search-api) from [SerpApi](https://serpapi.com/):

```javascript
//serpApiHandler.js
import dotenv from "dotenv";
import { config, getJson } from "serpapi";

dotenv.config();
config.api_key = process.env.API_KEY; // your SerpApi private key from .env file

// make params to make a request to SerpApi depending on target store
const getParams = (store, query) => {
  switch (store) {
    case "ebay":
      return {
        _nkw: query,
      };
    case "walmart":
      return {
        query,
      };
    default:
      break;
  }
};

// get JSON with results and return organic_results array
const getStoreResults = async (store, query) => {
  const params = getParams(store, query);
  const results = await getJson(store, params);
  return results.organic_results;
};

export default getStoreResults;
```

After we get organic results (for example from eBay) we filter it, leaving only products which have a buy now price, are in new condition, and match our search query with Levenshtain distance method. And finally sort them in ascending order of price:

```javascript
//storeResultsHandler.js
const ebayFiltering = (query, accuracy, results) => {
  return results
    ?.filter((result) => {
      const { title, condition, price } = result;
      const extractedPrice = price?.extracted;
      if (100 - levenshtein(query, title) >= accuracy && condition === "Brand New" && extractedPrice) {
        return true;
      } else return false;
    })
    ?.sort((a, b) => {
      if (a.price.extracted > b.price.extracted) return 1;
      else return -1;
    });
};
```

After receiving the results, the user selects those that suit him and again sends a request to the server on route `"/comparison"`:

```javascript
//index.js
app.post("/comparison", buildComparisonTable);
```

This time, our server, using SerpApi, searches and filters products on another site (if eBay was selected as the target site, then the search is performed on Walmart) for each product selected by the user:

```javascript
//storeResultsHandler.js
export const buildComparisonTable = async (req, res) => {
  const { targetStore, selectedResults, accuracy } = req.body;
  let comparisonResults = [];
  for (const result of selectedResults) {
    const { title } = result;
    if (targetStore === "ebay") {
      const walmartResults = await getStoreResults("walmart", title);
      comparisonResults.push({ product: result, comparison: walmartFiltering(title, accuracy, walmartResults) });
    } else {
      const ebayResults = await getStoreResults("ebay", title);
      comparisonResults.push({ product: result, comparison: ebayFiltering(title, accuracy, ebayResults) });
    }
  }
  res.status(200).json({ comparisonResults });
};
```

The final result is returned to the user for the selection of products with the greatest benefit and the ability to save the selected products in the "xlsx" file:

![image](https://user-images.githubusercontent.com/64033139/215344611-9f36eb92-3aab-4d88-b410-f4904a6b9be7.png)

<h2 id='limitations'>Limitations & things to improve</h2>

Limitations:

- You need to specify the name of the product as accurately as possible;
- You need to choose the appropriate search results yourself;
- Only two marketplaces are currently supported;
- Ability to save results only in `xslx` format.

Things to improve:

- Connect more marketplaces;
- Create a PDF template to make saved results look prettier and more readable;
- Use AI for result matching instead of `levenshtein`. Possibly match via images also.

<h2 id='links'>Links</h2>

- [Walmart Search Engine Results API](https://serpapi.com/walmart-search-api);
- [Ebay Search Engine Results API](https://serpapi.com/ebay-search-api);
- [Project frontend repository](https://github.com/Mykhailo-Zub/comparison_app);
- [Project backend repository](https://github.com/Mykhailo-Zub/comparison_server);
- The backend is hosted on [Vercel](https://vercel.com/);
- The frontend is hosted on [Netlify](https://www.netlify.com/);

If you want other functionality added to this demo project or if you want to see some other projects made with SerpApi, [write me a message](mailto:miha01012019@gmail.com).

---

<p style="text-align: center;">Join us on <a href="https://twitter.com/serp_api">Twitter</a> | <a href="https://www.youtube.com/channel/UCUgIHlYBOD3yA3yDIRhg_mg">YouTube</a></p>
<p style="text-align: center;">Add a  <a href="https://github.com/serpapi/public-roadmap/issues">Feature Request</a>💫 or a <a href="https://github.com/serpapi/public-roadmap/issues">Bug</a>🐞</p>