Obinna Ekwuno Writer of all things technical and inspirational. Developer and Community Advocate. In a love-love relationship with JavaScriptđŸ”„

Gatsby APIs you need to know

4 min read 1352

Gatsby APIs you need to know

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.

How does Gatsby work?

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 using async/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

Gatsby APIs

Extend schema capabilities

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.

Add a third-party schema

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,
  }
},

Extend/transform nodes from other nodes

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.

Custom Webpack configurations

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.

replaceWebpackConfig

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.

Conclusion

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. 😄

Get setup with LogRocket's modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.
  3. $ 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>
  4. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • ngrx middleware
    • Vuex plugin
Get started now
Obinna Ekwuno Writer of all things technical and inspirational. Developer and Community Advocate. In a love-love relationship with JavaScriptđŸ”„

Leave a Reply