ScottSpence.com

Scott's Thoughts Pamphlet!

Using the GitHub GraphQL API in a serverless function - GraphQL vs REST

This isn’t a guide, this is me documenting the how and why for a proof of concept project I worked on.

So, I started playing around with the GitHub GraphQL API endpoint to do something I’ve been meaning to do for a wile now, that’s to make a serverless function that will return the GitHub stats from my GitHub profile.

Me doing this came up in conversation with my friend Paul Scanlon and he decided to do something similar but with the GitHub REST API, so we are making notes on how things are going!!

What is it you’re doing though?

What I’m doing is based off of several Leigh Halliday videos on using the GitHub GraphQL API and adding that data to a pie chart.

My current site has a similar pie chart in the about section which created at build time. This chart is created at request time from the client (the browser).

Check out the Interactive example for an idea of what’s going on.

ℹ Also Leigh has just released Next Level Next.js where you can get $10 off with my affiliate link.

Approach

The idea is to use a serverless function that will take in a GitHub usename and use that to query the GitHub GraphQL API and return data related to the username.

The API data will then be used to populate a pie chart showing the language split for that users repositories on their GitHub account.

Once that is done the serverless function will render the chart in HTML and take a picture of the chart and return that as the response to the client.

The serverless provider I’m using is Vercel, which in turn uses AWS Lambda. This means that the code I want to run is triggered by an incoming request to the code on the Vercel servers.

When I say incoming request I mean a URL, something like:

1https://serverless.vercel.app

Where the GitHub username would be passed in the request as a variable and used for the GraphQL query, so the URL in my case would look something like this:

1https://serverless.vercel.app?username=spences10

The code on Vercel then parses the request, takes out the username variable and passes that to a GraphQL query in Axios which returns a JSON object. The JSON is then manipulated to use in the pie chart.

The query

The GitHub GraphQL API query looks like this:

1query GITHUB_USER_DATA_QUERY($username: String!) {
2 user(login: $username) {
3 repositories(
4 last: 100
5 isFork: false
6 orderBy: { field: UPDATED_AT, direction: ASC }
7 privacy: PUBLIC
8 ) {
9 nodes {
10 name
11 description
12 url
13 updatedAt
14 languages(first: 5) {
15 nodes {
16 color
17 name
18 }
19 }
20 }
21 }
22 }
23}

This query will give the last 100 repos that aren’t forks and are publically viewable for that GitHub username.

Here’s what the response from the GraphQL query looks like:

1{
2 "data": {
3 "user": {
4 "repositories": {
5 "nodes": [
6 {
7 "name": "scottspence.com",
8 "description": "My Letter Beautiful Mysterious Notebook.",
9 "url": "https://github.com/spences10/scottspence.com",
10 "updatedAt": "2021-01-18T17:35:19Z",
11 "languages": {
12 "nodes": [
13 {
14 "color": "#f1e05a",
15 "name": "JavaScript"
16 }
17 ]
18 }
19 }
20 ]
21 }
22 }
23 }
24}

Map filter reduce

The JSON data from the GraphQL call is then transformed so it will go into the data shape the pie chart is expecting.

Check out the data transform module on the repo for more detail and also Leigh’s video Map, Reduce, Filter, and Pie Charts is super helpful.

Charts

I went with Google chart library first as it had what I needed (Pie/Doughnut and Heatmap) but it’s not responsive. This isn’t a big deal as the chart is being returned as an image.

To work with the chart locally I used the live server VS Code extension and a added the chart to an index.html file to get an idea of how it will look.

This really helped with getting the data from the API response to fit with what the graph was expecting changing the data transform function accordingly.

Here’s a small sample of the script in the HTML file that indicates the data it’s expecting:

1<script type="text/javascript">
2 google.charts.load('current', { packages: ['corechart'] })
3 google.charts.setOnLoadCallback(drawChart)
4 function drawChart() {
5 var data = google.visualization.arrayToDataTable([
6 ['Languages', 'Languages Count'],
7 ['JavaScript', 37],
8 ['TypeScript', 13],
9 ['CSS', 12],
10 ['HTML', 7],
11 ])
12
13 var options = {
14 // title: 'My Languages Split',
15 colors: ['#f1e05a', '#2b7489', '#563d7c', '#e34c26'],
16 chartArea: {
17 left: 0,
18 top: 30,
19 width: '100%',
20 height: '90%',
21 },
22 }
23
24 var chart = new google.visualization.PieChart(
25 document.getElementById('doughnut')
26 )
27 chart.draw(data, options)
28 }
29</script>

Take a picture

Now that the data looks ok in the pie chart I want to take a picture of it from a browser, but it’s on a server, so I need to use a headless browser like Chromium.

To do this I used Puppeteer I wanted to use Playwright but that didn’t work on Vercel so a reverted to Puppeteer like with the serverless open graph image project I made a while back now.

Latency

Loading the image does take a while, I’ve added this one below the fold but because it’s not part of Gatsby image there will be layout shift unless I add a default heihgt to the img tag.

GitHub contributions pie chart

Becuse this isn’t being done at build time there is a noticeable delay in the image being served sometimes.

There may be something I can do about it with some persisted queries with OneGraph, not for this post though.

Interactive example

You can check out the latency by changing my username in the live code example here:

Big boi function

I was poking around with the function on Vercel and found that the size was at 90% 😬 the max size for AWS functions is 45mb, 99% od that is taken up by Chrome AWS lambda anf d Puppeteer core.

This didn’t really bode well as I’d only finished one part of it, the next part will be a heat map.

Colour contrast on the graph

There was some contrast issues with the text on the pie chart so I had to find a way to change the contrast of the text color.

After a bit of searching I found contrast-color-generator which offered up a colour to satisfy the W3C guidelines.

This then had to be added to the data transform to change the colour of the text.

Here’s a small snippet of how the languages are addd to an object:

1const langaugesArray = Object.entries(langObject).map(
2 ([key, value]: any) => {
3 return {
4 id: key,
5 label: key,
6 value: value.count,
7 color: value.color,
8 textColor:
9 value.color !== null
10 ? generator.generate(value.color).hexStr
11 : '#000000',
12 }
13 }
14)

Compared to the REST API

Paul has done a great write up on the contrast between the two approaches.

I have outlined where my approach isn’t great with the latency from the function but there’s a lot going on with that.

From what I could glean from Paul’s approach is that the languages are only the most used language whereas the GraphQL response is all the languages used in and object.

If I used the REST API on the serverless function there would probably be the same amount of latency.

Resources

Back to Top


Scott Spence

Built with Gatsby · Hosted on Vercel · 2021