graphql-engine/community/tools/graphql2chartjs/how-it-works.md
2019-03-14 14:30:41 +05:30

5.3 KiB

How it works

Motivation

We started using ChartJS with GraphQL so that we could leverage GraphQL's realtime subscriptions to build realtime charts. Soon enough we realised that we can automate this tedious procedure of restructuring the GraphQL data to a form that ChartJS expects.

The idea behind this tool is to generate ChartJS compliant data object from your GraphQL response by simply adding a few aliases in your GraphQL query.

GraphQL Aliasing

GraphQL gives you the power of aliasing the response fields with custom names. Lets look at a simple GraphQL query.

query {
    rootField {
        field1
        field2
    }
}

The response to this query would be of the form:

{
    "data": {
        "rootField": [
            {
                "field1": "value 1",
                "field2": "value 2"
            }
        ]
    }
}

Now, when we alias the above GraphQL query like so:

query {
    aliasedRootField: rootField {
        aliasedField1: field1
        aliasedField2: field2
    }
}

The response would be:

{
    "data": {
        "aliasedRootField": {
            "aliasedField1": 'value 1',
            "aliasedField2": 'value 2'
        }
    }
}

ChartJS API

Most of the ChartJS charts expect a data object of the form:

{
    "labels": ["label1", "label2", ..., "label10"], // list of strings
    "datasets": [ // list of custom datasets with their properties
        {
            "data": [1334, 4314, ..., 2356],
            "backgroundColor": ['red', "blue", ..., "brown"],
            "borderColor": ['red', "blue", ..., "brown"],
            "fill": false
        } 
    ]
}

The graphql2chartjs function

The graphql2chartjs function i.e. the default export of this library accepts two arguments:

  1. type: (String) Type of the chart; Eg. bar, line, pie
  2. graphqlData: [Object] This should be an object with each field having its value as a list of data points.

You can directly feed the output of the graphql2chartjs function to your ChartJS instance.


const graphQLResponse = makeGraphQLQuery();
var chartType = 'bar';

var myChart = new Chart(ctx, {
    type: chartType,
    data: graphql2chartjs(chartType, graphQLResponse),
    options: {...} //custom options
});

How the restructuring works

The graphql2chartjs function understands the API for each kind of chart that it supports. It constructs appropriate arrays mapping the indices of labels with other dataset properties.

Lets consider this GraphQL response:

{
  "data": {
    "VideoGameFollowers": [
      {
        "id": 1,
        "label": "Dota",
        "data": 427014,
        "pointBackgroundColor": "red",
        "fill": false
      },
      {
        "id": 2,
        "label": "CS:GO",
        "data": 220006,
        "pointBackgroundColor": "yellow",
        "fill": false
      },
      {
        "id": 3,
        "label": "NFS",
        "data": 71004,
        "pointBackgroundColor": "#3366ff",
        "fill": false
      },
      {
        "id": 4,
        "label": "PUBG",
        "data": 129769,
        "pointBackgroundColor": "#330000",
        "fill": false
      },
      {
        "id": 5,
        "label": "Quake 3",
        "data": 90808,
        "pointBackgroundColor": "green",
        "fill": false
      }
    ]
  }
}

The above GraphQL response is restructured to the ChartJS data as follows:

  1. It starts with initializing the data object as:

    {
        "labels": [],
        "datasets": []
    }
    
  2. It pushes a dataset with label as humanized(rootFieldName). In this case, the root field is VideoGameFollowers. After inserting this step, the data object looks like

    {
        "labels": [],
        "dataset": [
            {
                "label": "Video game followers"
            }
        ]
    } 
    
  3. It then iterates over the contents of this dataset. For each datapoint in the dataset, it pushes the label to the top level labels array and every other property to the dataset. So, after inserting the first data point, that is:

    {
      "id": 1,
      "name": "Dota",
      "data": 427014,
      "pointBackgroundColor": "red",
      "fill": false
    }
    

    the data object looks like:

    {
        "labels": ["Dota"],
        "datasets": [
            {
                "data": [427014],
                "pointBackgroundColor": ["red"],
                "fill": false
            }
        ]
    }
    

    As you see, pointBackgroundColor and data get pushed in an array while fill gets set as a top level field. This is because graphql2chartjs function understands that the ChartJS API expects pointBackgroundColor to be an array and fill to be a simple flag.

  4. It repeats the step above for every data point. The final data object would be:

    {
      "labels": [ "Dota", "Cs:go", "Nfs", "Pubg", "Quake 3"],
      "datasets": [
        {
          "label": "Video game followers",
          "id": 5,
          "data": [ 427014, 220006, 71004, 129769, 90808 ],
          "pointBackgroundColor": ["red", "yellow", "#3366ff", "#330000", "green"],
          "fill": false
        }
      ]
    }
    

Now you can pass this data object to your ChartJS instance and you will have a chart like this:

line-chart-example