Introduction

The project is available here. Google provides us with a google trends API to look for what people are searching on Google at the moment. In this tutorial, I will tell you how to use google trends API to build a react app to graphically compare between two keywords being searched on google over the years using google trends API. A lot of the time we search keywords independently but sometimes we search them in conjunction. While the keywords tool visualizations of different keywords, this project will show each keyword’s graph and the graph of cases where the string concatenation of the keywords was searched.

Motivation

A thought came into my mind, what are people all across the globe searching on Google. Then I came to know we can find out what the people are searching on Google using a tool provided by Google itself known as Google Trends. Then I came across this google trends API using which we can also create our application similar to Google Trends and add some unique functionalities to it not offered by Google.

Goal

My goal with this project is to make the readers successfully implement it with the greatest ease and then deploy it online for free.

Glossary

scatter chart: type of plot or mathematical diagram using Cartesian coordinates to display values for typically two variables for a set of data.

trends: a general direction in which something is developing or changing.

Project Requirements

  1. Basic programming fundamentals are a must
  2. Must have a little bit idea about JavaScript, React.js, Node.js and npm packages

Step by Step Tutorial

Backend

  1. In this, we are going to create only one file that will be our server file in which we will define our routes.
  2. We will define two routes, the first one is /:keyword1/:keyword2 and the second is /:country. In the first one, the google trends API is going to return the trend data of keyword1, keyword2 and their concatenation that is keyword1 + " " + keyword2 . In the second route, the google trends API will return a list of trending searches based on your location/ country.
  3. The server.js file looks like this
    const googleTrends = require('google-trends-api');
    var express = require('express')
    var cors = require('cors')
    var app = express()
    app.use(cors())
    app.get("/", async (req, res) => {
    res.json({
    'Ping': 'Pong'
    })
    })
    app.get("/:keyword/:keyword2", async (req, res) => {
    try {
    //console.log('reached')
    var result = [];
    var result2 = [];
    var result3 = []
    googleTrends.interestOverTime({
    keyword: req.params.keyword
    })
    .then(function(results) {
    // console.log((JSON.parse(results).default.timelineData[0]));
    JSON.parse(results).default.timelineData.map((data, i) => {
    result.push({
    'date': data.formattedTime,
    'value': data.value[0]
    })
    })
    }).then(function() {
    googleTrends.interestOverTime({
    keyword: req.params.keyword2
    })
    .then(function(results) {
    // console.log((JSON.parse(results).default.timelineData[0]));
    JSON.parse(results).default.timelineData.map((data, i) => {
    result2.push({
    'date': data.formattedTime,
    'value': data.value[0]
    })
    })
    }).then(function() {
    googleTrends.interestOverTime({
    keyword: req.params.keyword + " " + req.params.keyword2
    })
    .then(function(results) {
    // console.log((JSON.parse(results).default.timelineData[0]));
    JSON.parse(results).default.timelineData.map((data, i) => {
    result3.push({
    'date': data.formattedTime,
    'value': data.value[0]
    })
    })
    var final = new Array(result.length + 1);
    final[0] = new Array(4);
    final[0][0] = "Years";
    final[0][1] = req.params.keyword;
    final[0][2] = req.params.keyword2
    final[0][3] = req.params.keyword + " " + req.params.keyword2;
    for (var i = 1; i < final.length; i++) {
    final[i] = new Array(4);
    final[i][0] = result[i - 1] && result[i - 1].date ? result[i - 1].date : "";
    final[i][1] = result[i - 1] && result[i - 1].value;
    final[i][2] = result2 && result2.length && result2[i - 1].value ? result2[i - 1].value : 0
    final[i][3] = result3 && result3.length && result3[i - 1].value ? result3[i - 1].value : 0
    }
    res.json(final)
    })
    })
    })
    } catch (err) {
    console.log(err)
    }
    })
    app.get('/:country', async (req, res) => {
    try {
    var result = []
    await googleTrends.dailyTrends({
    geo: req.params.country
    }).then(function(results) {
    var arr = JSON.parse(results).default.trendingSearchesDays[0].trendingSearches
    for (var i = 0; i < arr.length; i++) {
    result.push(arr[i].title.query)
    }
    res.json(result)
    })
    //res.json({'trend1':trend1,'trend2':trend2})
    } catch (err) {
    console.log(err)
    }
    })
    app.listen(process.env.PORT || '3001', function() {
    console.log("Server started!!")
    })
    view raw server.js hosted with ❤ by GitHub
  4. With this the backend is done, let’s move on to the frontend.

Frontend

  1. The first step is to create a react-app (use npx create-react-app appname)
  2. Now set up redux in the app (follow this short video for help)
  3. Now after everything is set, let’s start with the frontend designing and setting up the actions and reducers.
  4. First Lets code the App.js file first for the design and this is how it looks
    import React, { useEffect, useState, Fragment } from "react";
    import Chart from 'react-google-charts'
    import { connect} from "react-redux";
    import {trendData, getTrending} from './actions/data'
    import {PropTypes} from 'prop-types'
    import loading from './loading.gif'
    import Dropdown from 'react-dropdown';
    import 'react-dropdown/style.css';
    const App = ({trendData,sending,dailyTrending, result, getTrending,keyArr}) => {
    useEffect(()=>{
    // console.log('Hello')
    async function getTrend(){
    await getTrending();
    }
    getTrend()
    },[getTrending])
    const handleTrend1 = async(e) => {
    //console.log(trending1)
    settrend1({
    ...trend1,
    trending1 : e.target.value
    })
    }
    const handleTrend2 = async(e) => {
    settrend2({
    ...trend2,
    trending2 : e.target.value
    })
    }
    const handleClick = async(e) =>{
    console.log('clicked')
    if(trending1.length !=0 && trending2.length !=0){
    trendData(trending1,trending2)
    }
    }
    const [trend1,settrend1] = useState({
    trending1:''
    })
    const {trending1} = trend1;
    const [trend2,settrend2] = useState({
    trending2:''
    })
    const {trending2} = trend2;
    const data = result.length == 0 || result.length==1 ? [[1,1,1,1]]: result;
    const options = {
    title: "Trend Comparison",
    curveType: "function",
    legend: { position: "bottom" }
    };
    const options2 = [
    'Line Chart', 'Scatter Chart'
    ];
    const defaultOption = options2[0];
    const[option,setOption] = useState({
    chart:'Line Chart'
    })
    const {chart} = option;
    return (
    <Fragment>
    <div className="container">
    <div className="jumbotron jumbotron-fluid">
    <div className="container">
    <h1 className="display-4" style={{"textAlign":"center"}}>Compare Google search trends</h1>
    </div>
    </div>
    <div className ="row">
    <div className="col-md-2 col-sm-12" style={{textAlign:'center'}}>
    {dailyTrending? <img src={loading} style={{width:"200px",height:"200px"}}/>: <div><h6>Trending Searches</h6><i class="fa fa-arrow-down" aria-hidden="true"></i>{keyArr.map((x)=>
    <div> {x}</div>
    )}</div>}
    </div>
    <div className="col-md-10 col-sm-12">
    <div style={{"textAlign":"center"}}>
    <textarea style={{"margin":"10px","width":"30%"}} placeholder = "Trend 1 keywords" onChange={handleTrend1}></textarea>
    <textarea style={{"margin":"10px","width":"30%"}} placeholder = "Trend 2 keywords" onChange={handleTrend2}></textarea>
    </div>
    <div style={{"textAlign":"center"}}>
    <button style={{margin:'10px'}} type="button" className="btn btn-success" onClick={handleClick}>Compare the two trends</button>
    </div>
    <div style={{"textAlign":"center"}}>
    <Dropdown className = "btn btn-light" style={{width:'fit-content !important'}} onChange = {(e)=>{setOption({
    ...option,
    chart:e.value
    })}} options={options2} value={defaultOption} placeholder="Select chart"></Dropdown>
    </div>
    <div style={{"textAlign":"center", "margin":"20px"}}>
    {sending ? <img src = {loading} /> :
    result.length ==1 && result[0] && result[0].length ? <h1>No comparisons found</h1>:
    <div>
    {chart=='Line Chart' ? <Chart
    chartType="LineChart"
    width="100%"
    height="400px"
    data={data}
    options={options}
    />: <Chart
    chartType="ScatterChart"
    width="100%"
    height="100%"
    data={data}
    options={options}
    legendToggle
    />}
    </div>}
    </div>
    </div>
    </div>
    </div>
    </Fragment>
    );
    }
    App.propTypes = {
    trendData:PropTypes.func.isRequired,
    sending:PropTypes.bool.isRequired,
    dailyTrending:PropTypes.bool.isRequired,
    result:PropTypes.array.isRequired,
    getTrending:PropTypes.func.isRequired,
    keyArr:PropTypes.array.isRequired
    }
    const mapStateToProps = (state) => ({
    sending:state.trend.sending,
    dailyTrending:state.trend.dailyTrending,
    result:state.trend.result,
    keyArr:state.trend.keyArr
    });
    export default connect(mapStateToProps, {
    trendData, getTrending
    })(App);
    view raw app.js hosted with ❤ by GitHub

    Don’t worry if you didn’t get meaning of the functions used, it will be way easier to understand once the actions and reducers are set
  5. Let’s write the actions corresponding to each of the 2 routes at the backend
    import axios from 'axios'
    import * as RNLocalize from "react-native-localize";
    // const domain = "http://localhost:3001&quot;
    const domain = "https:apigoogletrends.herokuapp.com"
    export const trendData = (trend1, trend2) => async (dispatch) => {
    try {
    dispatch({
    type: 'SENDING'
    })
    console.log('dispatched')
    const country = RNLocalize.getCountry();
    const result = await axios.get(`${domain}/${trend1}/${trend2}`)
    // const result2 = await axios.get(`${domain}/${trend2}`)
    // console.log(result.data)
    dispatch({
    type: 'TREND_RECEIVED',
    payload: result.data
    });
    } catch (error) {
    if (error.response) {
    console.log(error.response.data.errors);
    console.log(error.response.status);
    console.log(error.response.headers);
    } else if (error.request) {
    /*
    * The request was made but no response was received, `error.request`
    * is an instance of XMLHttpRequest in the browser and an instance
    * of http.ClientRequest in Node.js
    */
    console.log(error.request);
    } else {
    console.log("Error", error.message);
    }
    console.log(error);
    dispatch({
    type: 'TREND_ERROR',
    });
    }
    };
    export const getTrending = () => async (dispatch) => {
    try {
    dispatch({
    type: 'TRENDING'
    })
    console.log('dispatched')
    const country = RNLocalize.getCountry();
    const result = await axios.get(`${domain}/${country}`)
    // const result2 = await axios.get(`${domain}/${trend2}`)
    // console.log(JSON.parse(result.data).default.trendingSearchesDays[0].trendingSearches)
    dispatch({
    type: 'TRENDING_RECEIVED',
    payload: result.data
    });
    } catch (error) {
    if (error.response) {
    console.log(error.response.data.errors);
    console.log(error.response.status);
    console.log(error.response.headers);
    } else if (error.request) {
    /*
    * The request was made but no response was received, `error.request`
    * is an instance of XMLHttpRequest in the browser and an instance
    * of http.ClientRequest in Node.js
    */
    console.log(error.request);
    } else {
    console.log("Error", error.message);
    }
    console.log(error);
    dispatch({
    type: 'TRENDING_ERROR',
    });
    }
    };
    view raw data.js hosted with ❤ by GitHub

    Note: I am using react-native-localize package to get the country name to give it as a parameter for the second route so that google trends API returns the list of trending searches of that location
  6. After writing the actions, now our last task is to write the reducer
    view raw trends.js hosted with ❤ by GitHub
  7. After this is done, import all the actions and the redux state variables in the App.js file (step 4)
  8. Now, the client application is ready for deployment (follow this for help) and it will finally look like
How to build a google search trends comparer using Google Trends API How to build a google search trends comparer using Google Trends API

Learning Tools and Strategies

  1. Don’t write messy code, everything should be well understood. Doing this will automatically help you when it comes to debugging.
  2. Do a console.log() at each major step you feel is important, or that you feel may throw some kind of error. This is so that you can get the result of each major code-snippet.

Reflective Analysis

It is a very cool project. At first, I thought Google must be charging for providing this data but it is free anyways There are many other things that you can do with the google trends API, which means the possibilities for the future are vast.

Conclusion

The project gives a detailed idea of how to implement a google trends API. There are many other things that you can do with google trends API. Do play around with this API. The server application and the client application are deployed separately, so I would suggest trying deploying them both together as a single application. Also, this project just graphically compares the search trends, so try adding a button that allows the user to download the comparison report instead of just visually comparing the search trends.

Citations

I used https://www.mltcreative.com/blog/keyword-research-isnt-dead-google-trends-is-keeping-it-that-way/ for the featured image

The project is available here and deployed here. If you want to code along, you can watch this video uploaded by me.