Welcome to my series on Gatsby development. I’ve been using Gatsby for about two years on and off and I find myself coming back to it whenever I need to make a new website. I want to share a few bits of learning that has been immensely helpful in understanding the capabilities of Gatsby. I will be focusing on the Gatsby data layer in this post.
GraphQL
To understand the data layer we will need to dive into the system Gatsby uses to query data. Gatsby uses GraphQL to enable components to declare the data they need. GraphQL was invented at Facebook to help product engineers pull needed data into components. It’s a query language which is what the QL part of its name stands for. It uses a type system known as the schema definition language(SDL).
Benefits of GraphQL over REST
With a REST API you typically gather the data you need for a given page by accessing multiple endpoints. For example you might use an endpoint to gather initial data /users/<id>
then a second endpoint to grab all the posts /users/<id>/posts
. However with GraphQL you’d only need to sed a single query to the GraphQL server that includes the concrete data requirements.
GraphQL in Gatsby enables the following:
- Automatically optimize images for faster loading and responsiveness.
- Allows you to create relationships with data so we don’t have to query each individual piece.
- No more over and under fetching. One of the most common problems with REST is over and under fetching. This happens because the only way for a client to download data is hitting endpoints that return fixed data structures.
- Myriad of source and transform plugins to source data from different data sources and formatting that in a way that can be used in your Gatsby application.
GraphQL Type System
GraphQL spec defines some built-in scaler values:
- Int
- Float
- String
- Boolean
- ID
Lists/arrays in the type system can be denoted by square brackets: [String!]
. This indicates that we are defining a list of items that are of type String
and the ! indicates that this is a required field.
Example of SDL
type Post {
id: String!
title: String!
publishedAt: DateTime!
likes: Int! @default(value: 0)
blog: Blog @relation(name: "Posts")
}
type Blog {
id: String!
name: String!
description: String
posts: [Post!]! @relation(name: "Posts")
}
Interface
An interface can be used to describe a type in an abstract way. It allows you to specify a set of fields that any concrete type which implements this interface needs to have. Below you’ll find a basic interface called Node and a type User which implements Node. Note that you need to implement all the fields present in an interface that another type inherits.
interface Node {
id: ID!
}
type User implements Node {
id: ID!
name: String!
age: Int!
}
Gatsby Data Loading
Now that you know a little bit about GraphQL and it’s SDL system. I have some good news you don’t need to go through the trouble of defining all the types that you intend to use in Gatsby.
A core feature of Gatsby is its ability to load data from anywhere to accomplish this Gatsby comes with an extensive set of plugins namely called source plugins.
Here you can view the list of available plugins. Most of the source plugins are prefixed with gatsby-source-*
. These plugins allow you to source data from the filesystem, CMSs, External GraphQL servers, databases, markdown.
Along with source plugins Gatsby comes with a lot of transform plugins that allow you to change the format of the data to be used in your graphQL query.
A few commonly used transform plugins in Gatsby are
gatsby-transformer-sharp
- used for processing your images in a variety of ways including resizing, cropping, creating responsive images, and provides a nodeImageSharp
to be queried in GraphQL.gatsby-transformer-remark
- used to parse Markdown files using Remark and provides a nodeallMarkdownRemark
to be queried in GraphQL.gatsby-transformer-json
- used to parse JSON files into GraphQL nodes.
If none of these plugins suite your needs you can define your own schema in gatsby-node.js
using the createTypes
action. Gatsby automaticly infers
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type AuthorJson implements Node {
joinedAt: Date
}
`
createTypes(typeDefs)
}
Gatsby automatically infers other fields in a defined type if you don’t include them from it’s source and transform plugins. if you want to be more explicit you can add @dontInfer
to the type definition.
When compiling your site at build time, Gatsby preprocesses all GraphQL queries it finds and prefetches data to be available. If you need to query data at runtime on the client side you can always use the the regular React way of doing data fetching with the useEffect
hook.
After all this if you still don’t want to use GraphQL with Gatsby you can still fetch data the Javascript way and still have it pre-processed at build time. By including the data in the context
object in the gatsby-node.js
file
exports.createPages = async ({ actions: { createPage } }) => {
// `getHackeNewsData` is a function that fetches our data
const allHackerNews = await getHackeNewsData(["programming", "javascript", "startup"])
// Create a page that lists the hacker news data.
createPage({
path: `/`,
component: require.resolve("./src/templates/all-hackernews.js"),
context: { allHackerNews },
})
// Create a page for each Pokémon.
allHackerNews .forEach(article => {
createPage({
path: `/hackernews/${article.name}/`,
component: require.resolve("./src/templates/hackernews.js"),
context: { article },
})
})
}
After reading this article hope you’ve gotten a better understanding of the data layer of Gatsby. Thanks for stopping by and keep reading for more Gatsby content.