Part 2: Create Nodes
Introduction
Time to get started with implementing the core part of a source plugin!
Gatsby exposes many Gatsby Node APIs (some people also refer to such APIs as “hooks”) that allow anyone, including source plugins, to hook into Gatsby’s build process. You can create pages, modify the bundler configuration, or in the case of a source plugin add data to Gatsby’s GraphQL data layer.
In this part of the tutorial, you’ll learn how to use the sourceNodes
Node API.
By the end of this part of the tutorial, you will be able to:
- Fetch data from an external backend API
- Insert that data into Gatsby’s GraphQL data layer
- Display your results inside a Gatsby site
The diagram below shows a high-level view of how a Gatsby source plugin works. (Don’t worry if this doesn’t make sense yet. You’ll learn about each step as you go.)
Expand for detailed description
To get data into Gatsby’s GraphQL data layer the source plugin goes through these steps:
- Fetch data from a remote REST/GraphQL API, content management system (CMS), database, or any other data source you can think of.
- Call
createNode
API to create GraphQL nodes. - Use these newly created GraphQL nodes for GraphQL queries in your site.
Source data from an API
Before making changes to your plugin, double check that you’re running the develop:deps
script in your terminal (or more general: That you’re recompiling your plugin on changes). Otherwise you’ll wonder why you don’t see your changes reflected in your example site (don’t ask how we know).
Add sourceNodes
lifecycle
Create a new file called source-nodes.ts
inside the plugin’s src
folder with a named sourceNodes
export:
You’re importing the TypeScript type GatsbyNode
from gatsby
. The GatsbyNode
type is a representation of all available Gatsby Node APIs and you can access each type with bracket notation. For example, you can get the TypeScript type for the onPluginInit
API like this: GatsbyNode[`onPluginInit`]
.
Each Gatsby Node API receives its available Node API helpers as its first parameter. The second parameter to the function is the plugin’s options.
In the example above you’re adding a log to the terminal output for initial testing purposes. You’ll learn more about plugin options and the reporter
API in Part 4.
Pro tip: You’re not immediately destructuring the Node API helpers in the function, but only in the function body. This way you’ll be able to more easily pass gatsbyApi
to utility functions.
A destructured version would look like this:
The problem with this is that if you want to pass reporter
or any other helper to a utility function, you’ll always need to update all function parameters when you add/remove a helper. gatsbyApi
contains everything and inside the helper function you can access what you need. You’ll see this applied in just a bit.
Next, add the sourceNodes
export to the plugin’s gatsby-node.ts
. Gatsby checks for named exports in gatsby-node
file, and only when those are given it runs the different Node APIs.
In a second terminal window run (or restart if it’s still running) the develop:site
script:
You should see the new log info Example plugin sourceNodes...
printed to the terminal.
Fetch data
After learning how to add the sourceNodes
API to your plugin, it’s time to do more than just outputting a message.
Open the source-nodes.ts
file, remove the previous reporter
usage, and import the fetchGraphQL
utility. You’ll use it to make requests against the example GraphQL API and it returns an object containing data
and errors
(you’ll only use data
for now).
The first argument to fetchGraphQL
is the endpoint, the second argument is the GraphQL query you want to make against the API.
For this tutorial you’ll be querying the Post
and Author
entities from the example API. You can see the query for that in the next step.
Working with a remote backend API
The topic of “working with a remote backend API” is a really broad topic and not something we can cover here in its entirety as part of this tutorial. However, here are some tips that will help you while working on your plugin:
- Feel free to either use a barebones fetch function (via
node-fetch
) or something more batteries-included likegot
. It depends on your API, e.g. if it’s unreliable, add retry logic to your fetching. If it’s your small API that you know, a more barebones approach will probably be just fine. - Check if the source you’re trying to access has its own SDK. Many CMSs have their own SDK, e.g. Contentful with their
contentful
package. When available, we’d recommend using these first. - It’s good practice to gracefully handle errors when trying to access a remote API. This includes errors directly from the API but also ones like sudden internet connection loss. You don’t want to silently fail on such errors but show them to the user of your plugin.
You can also explore the API if you’re not familiar with it. For example, you can visit http://localhost:4000/graphql
to explore the example API of this tutorial. It’s a fully functional GraphiQL IDE window and with its help you can create your queries and test them. Check if your remote API has interactive documentation or other forms of exploring the API.
Add the GraphQL query to the second parameter of fetchGraphQL
and add the correct TypeScript types:
To see if you can successfully source the data from the API, console.dir
the data
result:
Pro tip: If you use console.log(data)
, the whole depth of the object won’t be shown. You can use console.dir
instead, with a depth
setting to display everything.
Restart the develop:site
script and if everything worked correctly, you should see something like this in your terminal output:
This means that you can successfully access data from the example API! Gatsby is smart enough to notice that you run a plugin with a sourceNodes
API but that it doesn’t generate any nodes. That’s why the warning is showing up. You’ll fix this in the next step.
Create GraphQL nodes
Before immediately diving into the node creation process, let’s take a step back and ask yourself: What do you want to create from data
inside Gatsby? Within the context of this tutorial, that question is simpler to answer — nodes of type Post
and Author
— but this won’t be the case for every API you’ll work with. While inspecting the data that is available, you’ll want to think of how to assemble different node types and the relationships they have to one another. It helps to write this down, outside of code, in plain English or even a diagram. Only after doing that should you start writing the code.
For instance, while it’s not the API you’ll be working with in this tutorial, here’s an example of an API where you’ll need to think about fitting node types:
- You’re requesting the REST API endpoint called
/authors
and it gives you back a list of authors, each with a unique ID. However, this API response doesn’t contain the details for each author, instead you’re supposed to call/author/<author-id>
to request additional information. - The strict 1:1 mapping of API endpoints to node types would mean you’d have
Author
andAuthorDetail
- However, you shouldn’t create
Author
andAuthorDetail
node types but instead only aAuthor
one. While creating theAuthor
one, access the information from the author detail endpoint and enrich theAuthor
information.
Need a refresher on what “nodes” are? Don’t worry!
Inside Gatsby’s data layer, information is stored in objects called nodes. A node is the smallest form unit of data in the data layer. Different source plugins create different types of nodes, each of which have their own properties. For example, gatsby-source-filesystem
creates File
nodes.
Also be sure to checkout the GraphQL concepts page if you need more information.
createNode
Besides fetching of the actual data, the creation of nodes is another important piece to a source plugin.
Key Gatsby Concept 💡
Be sure to read this section carefully as understanding how createNode
works is really important for the rest of this tutorial and for source plugins in general.
A basic usage of createNode
looks like this:
Explanation on each required node field
Here’s a short explanation on each required field for your convenience:
id
: The node’s ID that Gatsby uses to track it. It must be a globally unique ID. You should use thecreateNodeId
helper to create it.internal
: An object that only allows a defined set of keys (see API reference)type
: The name of the node typecontentDigest
: A hash (or short digital summary) of the contents of a node. The digest should be unique to the node since it’s used for caching. If the node changes, this digest should also change. You can use thecreateContentDigest
helper to create that hash.If your node contains information like a revision ID or a
lastUpdated
timestamp, you should use this instead of creating your own content digest. This will lead to lower CPU utilization and still achieve the goal of invalidating the cache for the node when said node changes.
You can use the createNode
API reference in the future if you’re creating more advanced node types, e.g. ones where you have to define the mediaType
.
Once the sourceNodes
API with its createNode
call is run one is able to query the TypeName
type with a hello
key:
Task: Define constants
Since you’ll need to use the name of your node types in a couple of places, it’s easy to introduce typos while using them. Create a constants.ts
file to define your names once and reuse them everywhere. This way you can also more easily change the names after the fact. Neat!
You don’t always need constants for your node types. It depends on your data source and the data you have to work with.
If you’re working with a more barebones API you might need to manually map endpoints and/or data to node types. Meanwhile, when you work with a CMS, node types will often be dynamic and defined through the CMS data. In those instances you won’t need to manually define types as the CMS will drive that.
Add the following to your new constants.ts
file:
TypeScript tip: You’re using a const
assertion here to make the object read-only and tell the compiler to infer the most specific type it can. In this specific instance you use as const
to enable an enum
-like pattern instead of directly using TypeScript’s enum
.
For the next step you’ll also need some additional TypeScript types, so go ahead and add the following to the types.ts
file:
You’re setting up Discriminated Unions here which will be used as the input type for the nodeBuilder
utility. It ensures that e.g. with type: "Author"
only the data
in the shape of IAuthorInput
can be passed in.
Task: Create nodeBuilder
utility
You’ll create a utility function called nodeBuilder
. Its purpose is to take in data, mutate it (if necessary), and then call createNode
for each node type with its respective data. You’re then calling nodeBuilder
for each of your entries from the API response, filling Gatsby’s data store with the data from the example API.
Now that you know how a barebones version of createNode
works, DRY (acronym for “Do not repeat yourself”) things up by creating the nodeBuilder
function inside source-nodes.ts
.
Import the
SourceNodesArgs
andNodeInput
type fromgatsby
and theNodeBuilderInput
type fromtypes.ts
. Also import theNODE_TYPES
constants you just created:TypeScript tip: The
gatsbyApi
that a Gatsby Node API receives isn’t always the same on each Node API. Therefore types in the naming scheme “Node API name” (in PascalCase) + “Args” exist, e.g.sourceNodes
=>SourceNodesArgs
,createSchemaCustomization
=>CreateSchemaCustomizationArgs
. Those utility TypeScript types are helpful when passinggatsbyApi
or parts of it to another function.Apply the same logic as the barebones
createNode
example to thenodeBuilder
function:- Generate a unique
id
, dependent on theinput.type
andid
frominput.data
- Fill out the necessary fields on the node (as explained in “Explanation on each required node field” above)
- Add your arbitrary data (in this case
...input.data
) - Call
createNode
Great, you wrote a reusable helper function to create nodes! Now it’s time to put it to use.
Pro tip: If your incoming data has keys that clash with fields reserved by Gatsby (in this example
id
), you can prefix those fields to still make them available. So theid
coming from the example API will be available at_id
. You don’t have to use a_
prefix, you can choose whatever you want (but we’d recommend keeping things consistent).TypeScript tip: You can use the
satisfies
operator to check against a desired shape while inferring the most specific type possible.- Generate a unique
Scroll up in
source-nodes.ts
to the place where you’re callingconsole.dir()
and remove that log. Replace it with a destructuring assignment ofdata
to get theposts
andauthors
results out of it (you can also set an empty array as a default value):So both
posts
andauthors
are arrays of objects that you should iterate over. And each individualpost
andauthor
will become a Gatsby node when you iterate over both arrays and callnodeBuilder
inside the loops:
Hopefully you can see the benefit of creating a helper function: You don’t have to repeat yourself inside the loops, making the code for sourcing data and creating nodes more readable, concise, and clear.
Display results in your site
You’ve created GraphQL nodes from your data — time to also display them in your site!
Restart the develop:site
script in your terminal. Once the development server is running, go to Gatsby’s GraphiQL IDE at http://localhost:8000/___graphql
(Need a refresher? Read the introduction to GraphiQL).
Inside GraphiQL, try a query against your newly created Author
and Post
nodes, for example:
You should see this result in your browser:
Great, you’ve successfully queried your data in a Gatsby site! As the last step of this part of the tutorial, you’ll create pages from all available posts as well as display them on the index page.
First, edit the index page to display all posts and link to their individual pages (which you’ll create in just a second):
After copying & pasting the above contents of the index page into your file, your editor might complain about Queries.IndexPageQuery
(TypeScript error). This type gets autogenerated through the GraphQL query at the bottom of the file. Your editor might need a second until it finds the type. If it doesn’t fix itself, a reload of the code editor often helps.
If you go back to your browser and navigate to http://localhost:8000
, your index page should look like this:
To add the individual pages that the cards link to, create a new file called {Post.slug}.tsx
inside site/src/pages
with the following contents:
If you now click on one of the cards, e.g. the “The first post” card you should be linked to http://localhost:8000/post-1/
and see the individual post:
Summary
Good job! You’ve finished the most important part of creating a source plugin.
Take a moment to think back on what you’ve learned so far. Challenge yourself to answer the following questions from memory:
- Which Gatsby Node API is the appropriate one to add data to Gatsby’s data layer?
- How do you fetch data from a remote API?
- What is the purpose of
createNode
? - How do you create GraphQL nodes from your data?
As a quick review, here’s the diagram outlining the process:
Expand for detailed description
To get data into Gatsby’s GraphQL data layer the source plugin goes through these steps:
- Fetch data from a remote REST/GraphQL API, CMS, database, or any other data source you can think of.
- Call
createNode
API to create GraphQL nodes. - Use these newly created GraphQL nodes for GraphQL queries in your site.
Key takeaways
- Source plugins pull data from their original location into Gatsby’s GraphQL data layer through the
sourceNodes
Node API. - When creating nodes with
createNode
you’ll need to think about reserved and required fields, modifying your incoming data if necessary. - You can create a utility function around
createNode
to more efficiently create nodes. - You can use your example site to immediately test out if your source plugin works correctly.
Share Your Feedback!
Our goal is for this tutorial to be helpful and easy to follow. We’d love to hear your feedback about what you liked or didn’t like about this part of the tutorial.
Use the “Was this doc helpful to you?” form at the bottom of this page to let us know what worked well and what we can improve.
What’s coming next?
In Part 3, you’ll explicitly define the GraphQL schema of your source plugin and create a foreign-key relationship between the Post
and Author
node types.