Automate Your Portfolio with the GitHub GraphQL API
Tags: graphql, react, javascript
Published: 2020-09-11 18:30:09

This is an overview of how I'm currently using GitHub's API to automate my [website](https://imjoshellis.com). I'm transitioning from graduating bootcamp to looking for a job, and I wanted to get the portfolio up and running as quickly as possible.

## The Idea

Build a portfolio site that showcases up-to-date project information without requiring too much direct maintenance.

## The Reason

1. Fast: Since I come from a design background, I knew I would overthink it and waste time if I built a static site
2. Efficient: I'm already trying to make my GitHub look as good as possible, why do the work twice?
3. Strategic: I wanted my website to showcase my ability to work with external APIs.
4. Practical: I believe most decision-makers will be going to my site just to see my projects, whether on GitHub or live, so making that easy for them is key.

## Planning the data

The first thing I did was decide what kind of information I wanted to put on [my website](https://imjoshellis.com). The good news is GitHub exposes virtually everything with their API.

I settled on the following:

+ Profile info
+ Three featured repos (my top three pinned repos)
+ Three most recent repos (only public, non-forks)

For the profile info, I'll be grabbing almost everything available:

+ Name
+ Avatar
+ Bio
+ Status
+ Status emoji
+ Location
+ Work Status
+ Education

For each repo, I settled on this data:

+ Tags
+ Title
+ Description
+ Image (via the OpenGraph image in settings)
+ Link to repo
+ Link to demo (if available)
+ Most recent commit message/time/branch

## Using the GitHub GraphQL API

If you haven't worked with GraphQL before, I highly recommend looking into some other tutorials since I won't be covering the basics here.

Essentially, the idea is when you make a Query (similar to a GET request), you can tell the server what parts of the data you want.

This means we can make very specific calls to the API that give us the most relevant information.

Let's start simple with my profile info query. Note: To actually make the queries and process the response, I'll be using [Apollo](https://www.apollographql.com/docs/react/get-started/).

```graphql
  query GetAbout {
   viewer {
      id
      name
      bio
      avatarUrl
      location
      url
      status {
        emojiHTML
        message
      }
      company
    }
  }
```

The response will be an object shaped just like the query, which makes it easy to work with. If I want my bio data, for example, I would just store the response in a variable using Apollo and call `response.viewer.bio`.

The `viewer` key refers to me since I'm using an API key to make authenticated queries.

My featured repository call is a bit more complicated:

```graphql
query GetFeaturedRepos {
    viewer {
      id
      pinnedItems(first: 3) {
        edges {
          node {
            ... on Repository {
              name
              description
              homepageUrl
              pushedAt
              url
              openGraphImageUrl
              usesCustomOpenGraphImage
              refs(refPrefix: "refs/heads/", last: 3) {
                nodes {
                  name
                  target {
                    ... on Commit {
                      history {
                        totalCount
                      }
                      messageHeadline
                      pushedDate
                    }
                  }
                }
              }
              repositoryTopics(first: 100) {
                edges {
                  node {
                    topic {
                      name
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
```

The first thing is I only want three of my pinned Repositories, so I use `pinnedItems(first: 3)` to limit the response. Then, GitHub uses a node/edge format, which I honestly still don't fully understand.

The gist of it is `pinnedItems` will return a list of `edges` which each have a `node`. Then on the `node`, you have to grab the repository with `... on Repository`, then you can access the repository fields.

You also do a similar thing for commits if you want to get that info.

Honestly, it took a ton of trial/error, Google, and playing with the [explorer tool](https://developer.github.com/v4/explorer/) to get things like `... on Repository` working properly.

My other call to get my most recent pushed repositories looks almost identical to the above since I want the same info about each repository. The main difference is the filter:

```graphql
viewer {
      id
      repositories(
        orderBy: { field: PUSHED_AT, direction: ASC }
        last: 3
        privacy: PUBLIC
        isFork: false
      ) {
        ...
      }
    }
```

In this case, I'm calling for all my repositories, ordering them by recent pushes, filtering out any that are private or forks, then grabbing three.

## Wrap Up

And that's it! Once you have the data, it's simply a matter of creating TSX to display it, along with some conditionals and fallbacks in case the data doesn't exist.

Like I said at the start, this is just an overview, not a comprehensive tutorial... If you want to do something similar, here are helpful resources to get you going:

### Resources

+ [Get Started with Apollo](https://www.apollographql.com/docs/react/get-started/)
+ [GitHub GraphQL API docs](docs.github.com/en/graphql/)
+ [Test queries in your browser with the GitHub GraphQL explorer](https://developer.github.com/v4/explorer/)
+ [My portfolio on GitHub](https://github.com/imjoshellis/imjoshellis.com)
+ [My portfolio website](https://imjoshellis.com)

## Bonus: Using the Dev.to API

I also set up a simple call to the Dev.to API to grab a list of my articles. Here's what that looks like in my BlogPosts React component:

```js
const [state, setState] = useState(undefined)
const DEVTO_API_URL = 'https://dev.to/api/'
  
useEffect(() => {
  fetch(DEVTO_API_URL + 'articles?username=imjoshellis', {})
    .then(res => res.json())
    .then((json: DevToArticles) => setState(json))
}, [])
```

## What About You?

What ways are you using APIs on your portfolio? I'd love to see what you've done!