In this tutorial, we'll be creating a Gatsby source plugin, which will fetch all contributors to a given Github repo. We'll also learn how to use that plugin in a Gatsby site to render a list of contributors.
Let's take a sneak peek ๐ of what we'll get in the end:
The Big Picture:
One question that you might ask is: What is a source plugin?
Source plugins "source data" from remote or local locations into a format that Gatsby can process.
First, we'll build a plugin with the following behavior:
- Make an API request to the Github API.
- Convert the data returned by the API to Gatsby's node system.
- Accept plugin options to customize how our plugin works.
And then, we'll do the following steps to using that plugin:
- Configure our plugin in our site's
gatsby-config.js
file. - Write GraphQL queries to access our plugin's data.
How to Create a Source Plugin for Gatsby
We'll follow a simple step-by-step process to build the plugin:
Set Up Projects for Plugin Development
We'll set up an example site and create a plugin inside it to begin building.
- Set up an example site
Let's create a new Gatsby site with the gatsby new
command, based on the hello world starter.
gatsby new example-site https://github.com/gatsbyjs/gatsby-starter-hello-world
This site generated by the new
command is where we install the plugin. It will give us a place to test the code for our plugin.
- Set up a source plugin
Let's create a new Gatsby plugin with the gatsby new
command, this time based on the plugin starter.
gatsby new source-plugin https://github.com/gatsbyjs/gatsby-starter-plugin
This command will create our plugin in a separate project from our example site.
- Install the plugin in the example site
We're going to install the plugin in the example-site to make sure it works. Gatsby can only run plugins that are included in gatsby-config.js
, so let's do that.
Here is what the config will look like:
module.exports = {
/* Your site config here */
plugins: [require.resolve(`../source-plugin`)],
}
Note: Restart the development server, after changing the gatsby-config.js
file. Otherwise, changes made to the config won't reflect in the project.
Source Data and Create Nodes
Now that we've setup the project for development, it's time to fill in the details.
The gatsby-node.js
file is the place where the logic responsible for sourcing the data lives.
In that file, Gatsby exposes an API sourceNodes. There we'll use a Gatsby function named createNode. As the name suggests, it handles the creation of a new node.
Let's add the following code in our gatsby-node.js
file:
// constants for your GraphQL Contributor type
const CONTRIBUTOR_NODE_TYPE = `Contributor`;
exports.sourceNodes = async (
{ actions, createContentDigest, createNodeId },
pluginOptions
) => {
// A Gatsby function responsible for `creating a node`
const { createNode } = actions;
let contributors = [];
try {
// loop through "contributors" and create Gatsby nodes
contributors.forEach((contributor) =>
createNode({
...contributor,
id: createNodeId(`${CONTRIBUTOR_NODE_TYPE}-${contributor.id}`),
parent: null,
children: [],
internal: {
type: CONTRIBUTOR_NODE_TYPE,
content: JSON.stringify(contributor),
contentDigest: createContentDigest(contributor)
}
})
);
} catch (error) {
console.error(error);
}
return;
};
Now we have all the structure and logic for creating gatsby nodes
in place.
Let's add the real thing, the logic for fetching real data from the Github API. We'll use node-fetch
to make an API call. You're welcome to use any alternative library of your choice.
Add it as a dependency by running:
npm install node-fetch
After installing the dependency, we'll make necessary changes to the gatsby-node.js
file. And after that, the code will look as follows:
const fetch = require("node-fetch");
// constants for your GraphQL Contributor type
const CONTRIBUTOR_NODE_TYPE = `Contributor`;
exports.sourceNodes = async (
{ actions, createContentDigest, createNodeId },
pluginOptions
) => {
const { createNode } = actions;
let contributors = [];
const repoName = "webiny/webiny-js";
// construct the API URL
const githubRepoURL = `https://api.github.com/repos/${repoName}/contributors`;
try {
// make an API call
const response = await fetch(githubRepoURL);
// convert response to JSON
const contributors = await response.json();
// loop through "contributors" and create Gatsby nodes
contributors.forEach((contributor) =>
createNode({
...contributor,
id: createNodeId(`${CONTRIBUTOR_NODE_TYPE}-${contributor.id}`),
parent: null,
children: [],
internal: {
type: CONTRIBUTOR_NODE_TYPE,
content: JSON.stringify(contributor),
contentDigest: createContentDigest(contributor)
}
})
);
} catch (error) {
console.error(error);
}
return;
};
Let's restart the example-site project and visit http://localhost:8000/___graphql
As we can see there is a new query allContributor
available for us to use.
As it is, our plugin works fine. But, there is a small problem. It only fetches the contributor of a particular Github repo. And there is no way to change that value, which makes it hard to reuse by others. Let's fix this right away.
We can pass options into a plugin through a gatsby-config.js
file.
Let's update the gatsby-config.js
file in our example-site. We'll change the plugin from a string to an object with a resolve
and options
key.
After the changes the file will look like this:
module.exports = {
/* Your site config here */
plugins: [
{
resolve: require.resolve(`../source-plugin`),
// These options will be available in Gatsby node API
options: {
repo: "webiny/webiny-js",
},
},
],
}
Now that we've added the repo as an option to our plugin, let's update our plugin source code to use that instead of the hard coded value.
We'll make the following change:
// ... some code
// use repo name from plugin options
const repoName = pluginOptions.repo;
// construct the API URL
const githubRepoURL = `https://api.github.com/repos/${repoName}/contributors`;
// remaing code is removed for brevity
Using Data From the Source Plugin in a Site
Now, our source plugin is ready. Let's use its data to render the contributors list in our example-site.
In our example-site, we'll now query data from pages.
Add the following code to file at example-site/src/pages/index.js
:
import React from "react"
import { graphql } from "gatsby"
import "../styles/main.css"
export default function Home({ data }) {
return (
<div className="container">
<h1 className="heading">
Creating a GitHub contributors widget for gatsby.js
</h1>
<div className="grid">
{data.allContributor.nodes.map(contributor => (
<div
className="contributor-card"
key={contributor.id}
onClick={() => {
window.open(contributor.html_url, "_blank", "noopener noreferrer")
}}
onKeyDown={event => {
if (event.key === "Enter") {
window.open(
contributor.html_url,
"_blank",
"noopener noreferrer"
)
}
}}
tabIndex="0"
role="button"
>
<img
src={contributor.avatar_url}
alt={contributor.login}
className="card__img"
/>
<h5 className="card__name">{contributor.login}</h5>
</div>
))}
</div>
</div>
)
}
export const query = graphql`
{
allContributor {
nodes {
id
login
contributions
url
type
html_url
gravatar_id
avatar_url
}
}
}
`
Here we're using the GraphQL query allContributor
to get all contributor's data. As mentioned earlier, this data is generated by our source plugin.
After making these changes our example-site home page will look something like this:
Summary
We've written a Gatsby plugin that:
- can be configured with an entry in your
gatsby-config.js
file - requests data from an API
- pulls the API data into Gatsby's node system
- allows the data to be queried with GraphQL
You did it!! ๐ You now know how to create a source plugin for Gatsby.
You're welcome to check out the complete source code for example-site and source plugin.
If you like the post please share it on Twitter. Webiny has a very welcoming Community! If you have any questions, please join us on slack. You can also follow us on Twitter @WebinyCMS.