How to properly fetch your personal GitHub stats

For about 2-3 years I was using the familiar github-readme-stats service to render an image of my GitHub contributions on my personal home page. It looked something like this:

kyle-west's public GitHub stats

This worked fine, but I had two issues with it:

  • I was doing a redesign of my website, and the card styles didn't quite match my new "CLI inspired" design
  • This card only reflects my public contributions, which represents very little of what I have accomplished

The solution for the private contribution count, according to anuraghazra, is to fork his project and use your own API key. I thought about doing that, but I didn't really want the card per say, I just wanted the data that was displayed in the card.

With my new design, I wanted a really simple layout describing my work (at least for the time being). Something like this:

  {TOTAL_COUNT} contributions across {REPO_COUNT} projects within the last year.
 
  {COMMIT_COUNT} commits. {PR_COUNT} pull requests. {REVIEW_COUNT} reviews.

And of course, COMMIT_COUNT + PR_COUNT + REVIEW_COUNT needs to equal TOTAL_COUNT, that's a given.

But where do I find that data?


Introducing GitHub API v4

I'm not going to lie, this took me way longer to find than it should have. I'm basically just duplicating the Contribution Chart on my github profile. I assumed that would be straight-forward. After hours of guess-and-check-and-stack-overflow, I decided to bite the bullet and just read the API doc directly (rather than follow the advice of random blogs).

I should have started with the docs. Though verbose, it had everything I needed.


Setting up the query

Most examples you will find online will show you how to get the contributionCalendar in its entirety. I wanted the summary info, so that took a bit of tailoring.

This is what I ended up with:

user(login: "kyle-west") {
  contributionsCollection {
 
    contributionCalendar {
      totalContributions # this is the TOTAL_COUNT for the year
    }
 
    # these are the totals for Commits/Reviews/etc
    # if you add all of these up together, they should equal `totalContributions` above
    totalCommitContributions
    totalIssueContributions
    totalPullRequestContributions
    totalPullRequestReviewContributions
    totalRepositoryContributions
 
    # These are the number of projects you participated in
    totalRepositoriesWithContributedCommits # likely, this is the one you care about the most
    totalRepositoriesWithContributedIssues
    totalRepositoriesWithContributedPullRequestReviews
    totalRepositoriesWithContributedPullRequests
 
    # useful for debugging, this shows you the contribution period
    # which should cover the last year
    startedAt
    endedAt
 
  }
}

Obviously, there is overlap on the totalRepositoriesWith* counts. So you will have to use your best judgement. I used totalRepositoriesWithContributedCommits to represent my "across {REPO_COUNT} projects within the last year" because most repositories I reviewed or submitted a PR to are also ones that I have committed to. I think there are only 3 repos that don't fit that case for me, and I don't care that much to fix it.

For complete transparency...

I'm not sure why, but my github profile shows slightly higher information than what the API returns. I'm pretty sure this is a date matching thing, but I haven't had a chance to confirm that for sure.


Using in a NextJS edge function

Once I had the query, the Edge function was super easy to set up. I made a new file called /api/gh/stats.js where I set up my query, auth, and fetch in one simple function.

const headers = {
  // Token stored in a .env file
  Authorization: `bearer ${process.env.GITHUB_TOKEN}`,
};
 
const body = {
  query: `query {
    user(login: "kyle-west") {
      ...
  `,
};
 
export default async function kwStats(req, res) {
  try {
    const response = await fetch("https://api.github.com/graphql", {
      method: "POST",
      headers,
      body: JSON.stringify(body),
    });
    const data = await response.json();
    return res.status(200).json(data);
  } catch (err) {
    console.error(err);
    return res.status(500);
  }
}

After that, I went into the Vercel dashboard and used their flow to make sure the GITHUB_TOKEN was correct and that preview deploys had access to it (you can find it under Project > Settings > Environment Variables).

I pushed up a commit to a PR and Vercel instantly deployed my page for testing. Worked like a charm!


Lessons learned

Just read the docs. I wasted so much time trying to shortcut actual learning. I would have been done a lot quicker if I would have just used GitHub's API Explorer from the beginning.

Hopefully this blog saves y'all time though! Happy coding!