The Gatsby ecosystem has a lot of collaboration going on. This can be attributed to its open-source nature, a lot of people send in pull requests to the Gatsby repository to contribute to documentation, the framework itself, or create starters/showcases — which are sites built with Gatsby that are displayed on the official website.
Most of the ecosystem leverages on building with Gatsby and that’s awesome, although there are a couple of APIs that Gatsby makes available for people who want to really extend the framework’s capability. In this blog post, we will be looking at some of these APIs and some use cases where they come in handy.
Gatsby offers the best ways to build for the web while being endlessly customizable and extensible for any purpose. The idea that Gatsby works only for a specific use case (blogs, side projects) is inaccurate as Gatsby offers a layer that allows it to extend its features, this would mean that as your application grows Gatsby’s capability grows with it while having performance in mind.
For example, if your application requires a more custom schema customization this is what Gatsby’s Schema Customization API provides. Also in the case of wanting to extend Webpack configurations for your platform or in the case that you are not already using a plugin that handles this, onCreateWebpackConfig
can be exported in the gatsby-node.js
file.
Let’s start by looking at the gatsby-confi
file which handles the plugin functionalities in a Gatsby application. In this file, we can define the site’s metadata and other general configurations. This file should be in the root of your Gatsby site.
If you created a Gatsby site with the gatsby new
command, there should already be a sample configuration file in your site’s directory. The configuration file should export a JavaScript object. Within this object, you can define several different configuration options. For example:
module.exports = { siteMetadata: { title: `Name of your application`, }, plugins: [ `Name-of-plugin`, { resolve: `Name-of-plugin`, options: { optionA: true, optionB: `Another option`, }, }, ], }
Note for plugin authors : If your plugin performs async operations (disk I/O, database access, calling remote APIs, etc.) you must either return a promise (explicitly using
Promise
API or implicitly usingasync
/await
syntax) or use the callback passed to the 3rd argument. Gatsby needs to know when plugins are finished as some APIs, to work correctly, require previous APIs to be complete first. – Gatsby docs
Earlier in this article, I had mentioned an API to help extend GraphQL Schema for your data, these features are usually needed by plugin authors, users trying to fix GraphQL schemas created by automatic type inference, developers optimizing builds for larger sites, and anyone interested in customizing Gatsby’s schema generation.
The API in this example is the a href="https://www.gatsbyjs.org/docs/node-apis/#createSchemaCustomization" target="_blank" rel="noopener">createSchemaCustomization
(available in Gatsby v2.12 and above), and sourceNodes
APIs.
A straight forward use case is when a particular field in the automatically generated scheme has a type that isn’t clearly defined. For example, the code block below is an automatically generated node representation in Gatsby’s GraphQL layer of an author:
type AuthorJson implements Node { id: ID! parent: Node! children: [Node!]! internal: Internal! name: String firstName: String email: String joinedAt: Date } // Below is the query that results to the Scheme above. [ { "name": "Doe", "firstName": "Jane", "email": "[email protected]", "joinedAt": "2018-01-01" } ]
It’s important to note, that the data in
author.json
does not provide type information about the author fields by itself.In order to translate the data shape into GraphQL type definitions, Gatsby has to inspect the contents of every field and check its type. The problem with this method is that it is time consuming and can also lead to a scalability issue. Also, if the values on a field are of different types Gatsby cannot decide which one is the correct one. A consequence of this is that if your data sources change, type inference could suddenly fail.
In the case that a new author is added we can see from the code block below that the type of joinedAt
is both Date
and String
values:
{ "name": "Doe", "firstName": "John", "email": "[email protected]", "joinedAt": "201-04-02" } ]
We can make sure that the schema will only show a Date
type by providing explicit type definitions to Gatsby with the createTypes
action. It accepts type definitions in GraphQL Schema Definition Language:
exports.createSchemaCustomization = ({ actions }) => { const { createTypes } = actions const typeDefs = ` type AuthorJson implements Node { joinedAt: Date } ` createTypes(typeDefs) }
Note: That the rest of the fields don’t have to be provided, they will still be handled by Gatsby’s type inference.
You can also take complete control of the scheme, take a look at the official documentation for more information.
Besides being able to customize existing schemas Gatsby offers the capability to import existing schemas from other applications without modification while building out the frontend. This is possible using the addThirdPartySchema
API, the schema has to be a graphql-js GraphQLSchema object.
It is important to note that this schema can easily break the main Gatsby schema so it’s advised to make sure it doesn’t happen (by e.g. namespacing the schema):
addThirdPartySchema: ( {schema }: { schema: GraphQLSchema }, plugin: IGatsbyPlugin, traceId?: string): IAddThirdPartySchema => { return { type: `ADD_THIRD_PARTY_SCHEMA`, traceId, payload: schema, } },
The “node” is the center of Gatsby’s data system. All data that’s added to Gatsby is modeled using nodes.
When a plugin is created to act on the Gatsby codebase it is referred to as a node creation.
There are different types of Gatsby plugins, as use cases arise plugins can be created to act on other plugins to converts data from one format (e.g. CSV, YAML) to a JavaScript object. They often follow the naming convention gatsby-transformer-*
. These plugins are known as transformer plugins, an example of this is the gatsby-transformer-yaml
.
The above uses the onCreateNode
API to extend or transform nodes created by other plugins. Most API code is implemented in the gatsby-node.js
file. Below is a code example of how Yaml is transformed into a JavaScript object:
const jsYaml = require(`js-yaml`) const _ = require(`lodash`) async function onCreateNode({ node, actions, loadNodeContent, createNodeId, createContentDigest, }) { function transformObject(obj, id, type) { const yamlNode = { ...obj, id, children: [], parent: node.id, internal: { contentDigest: createContentDigest(obj), type, }, } createNode(yamlNode) createParentChildLink({ parent: node, child: yamlNode }) } const { createNode, createParentChildLink } = actions if (node.internal.mediaType !== `text/yaml`) { return } const content = await loadNodeContent(node) const parsedContent = jsYaml.load(content) parsedContent.forEach((obj, i) => { transformObject( obj, obj.id ? obj.id : createNodeId(`${node.id} [${i}] >>> YAML`), _.upperFirst(_.camelCase(`${node.name} Yaml`)) ) }) } exports.onCreateNode = onCreateNode
To get more information on how this API works, you can check out the official Gatsby documentation.
Your custom Webpack configurations can be a plugin to support your particular use case and can also be contributed to the community. If you would like to handle your bundling logic this can be done in the gatsby-node.js
using onCreateWebpackConfig
.
By default, Gatsby handles multiple Webpack builds with a somewhat different configuration. When Gatsby creates its Webpack config, this function will be called allowing you to modify the default Webpack config using webpack-merge.
It is possible to take full control of the config merging logic yourself using the replaceWebpackConfig
:
actions.replaceWebpackConfig = () => { return { type: `REPLACE_WEBPACK_CONFIG`, plugin, payload:config, } }
This can be dangerous and break Gatsby if plugin options are changed or there are conflicts with original plugin configurations in gatsby-config.js
. Generally, this is only useful for cases where you need to handle config merging logic yourself in which case consider using webpack-merge
.
In this article, we have looked at some APIs that can help to create plugins and extend functionality for Gatsby with a more accessible approach. We have also looked at code examples that show implementation. I hope to see some more implementations using these APIs.Happy coding. 😄
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.