All Articles

How to load GitHub projects with GraphQL in Gatsby

Alt

Hi there 👋

I’m Andrew, a Software Engineer, and a person who contributes to open source a lot. I have a blog based on gatsby and forked from lumen - the fast and flexible framework that makes building websites with any CMS, API, or database fun again.

Today I’m having fun with GraphQL for the first time in my life.

What is GraphQL?

GraphQL is a query language for your API and a server-side runtime for executing queries using a type system you define for your data. GraphQL isn’t tied to any specific database or storage engine and is instead backed by your existing code and data.

Playing with GraphQL

Goal: fetch 10 public repositories, sorted by stars, descending order.

Here is Github GraphQL Explorer, it will help us with testing GraphQL queries.

First of all, take a look at a reference to API objects to determine what we need to use for pulling repositories. Let’s try to run the easiest query - fetch the first 10 repositories using explorer.

viewer {
  repositories(first: 10) {
    edges {
      node {
        id
        name
        url
      }
    }
  }
}

Now, add a bit more complexity to the filtering. I want to see only public repositories I created and repositories from organizations I’m in. The following block into the repositories filter:

ownerAffiliations: [OWNER, ORGANIZATION_MEMBER], privacy: PUBLIC, isFork: false, isLocked: false

Adding ordering by stars:

orderBy: { field: STARGAZERS, direction: DESC }

Adding more data for fetching: stars, forks, owner, description, and the first 3 languages used in the repository. So, here is the final query we have:

  viewer {
    repositories(ownerAffiliations: [OWNER, ORGANIZATION_MEMBER], privacy: PUBLIC, isFork: false, isLocked: false, first: 10, orderBy: { field: STARGAZERS, direction: DESC }) {
      edges {
        node {
          id
          name
          url
          owner {
            login
          }
          description
          stargazers {
            totalCount
          }
          forkCount
          languages(first: 3) {
            nodes {
              id,
              name
            }
          }
        }
      }
    }
  }

For now, we have the GraphQL query, and the last thing is to adapt it to gatsby’s website.

Gatsby

For running GraphQL queries outside our website, we need to install and configure gatsby-source-graphql.

The next step is extending Gatsby with a new page, we need to add a new template under ./src/templates folder. Let’s call it projects-template.js with the following content:

import React from 'react';
import { graphql } from 'gatsby';

const ProjectsTemplate = ({ data }) => {
  const { title: siteTitle, subtitle: siteSubtitle } = useSiteMetadata();
  const { html: pageBody } = data.markdownRemark;
  const { frontmatter } = data.markdownRemark;
  const {
    viewer: {
      repositories: { edges },
    },
  } = data.github;
  const { title: pageTitle, description: pageDescription = '', socialImage } = frontmatter;
  const metaDescription = pageDescription || siteSubtitle;
  const socialImageUrl = socialImage?.publicURL;

  return (
    <Layout title={`${pageTitle} - ${siteTitle}`} description={metaDescription} socialImage={socialImageUrl} >
      <Sidebar />
      <Page title={pageTitle}>
        <div dangerouslySetInnerHTML={{ __html: pageBody }} />
        <Projects edges={edges}/>
      </Page>
    </Layout>
  );
};

export const query = graphql`
  query ProjectsPage($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      id
      html
      frontmatter {
        title
        date
        description
        socialImage {
          publicURL
        }
      }
    }
    github {
      viewer {
        repositories(ownerAffiliations: [OWNER, ORGANIZATION_MEMBER], privacy: PUBLIC, isFork: false, isLocked: false, first: 10, orderBy: { field: STARGAZERS, direction: DESC }) {
          edges {
            node {
              id
              name
              url
              owner {
                login
              }
              description
              stargazers {
                totalCount
              }
              forkCount
              languages(first: 3) {
                nodes {
                  id,
                  name
                }
              }
            }
          }
        }
      }
    }
  }
`;

export default ProjectsTemplate;

After creating the template, we also need to extend Gatsby’s createPages method, I have it located in ./gatsby/create-pages.js.

Inside the section where gatsby iterates over the content pages, let’s add the following code:

const { edges } = result.data.allMarkdownRemark;

  _.each(edges, (edge) => {
    // ...
    if (_.get(edge, 'node.frontmatter.template') === 'projects') {
      createPage({
        path: edge.node.fields.slug,
        component: path.resolve('./src/templates/projects-template.js'),
        context: {
          slug: edge.node.fields.slug,
          projectsCount: siteConfig.author.projects.count * 2,
        },
      });
    }
  });

Since that, we have 3 different markdown templates: post, page, and the new one projects.

It’s time to create a new markdown page with content for the projects page in the path ./content/pages/projects.md:

---
title: "Projects"
template: "projects"
socialImage: "/media/robo-andrew.jpg"
---

Here is the list of github projects I've contributed to:

The following part template: "projects" defines for createPages method what template should be used, therefore Gatsby needs to load the projects template from ./src/templates/projects-template.js.

Okay, now all the bits are connected to each other. Let’s see the result 👇

Public GitHub projects from my profile are pulled using GraphQL api and integrated into Gatsby’s blog.

Github Projects Example at andrew.red

Check out the live version on the projects page.

Interested in more Gatsby or GraphQL?

Find out useful content at:

Any feedback, contribution, or help is highly appreciated.

Thanks for your time and happy coding! 👋

Resources

Published Apr 21, 2022

Passionate software engineer with expertise in software development, microservice architecture, and cloud infrastructure.