Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to give Gatsby a GraphQL schema

We're bringing in some posts from a Wordpress backend, some have pictures (in an ACF field) and some don't. The problem is that Gatsby infers the schema based off of the first node it receives. If it receives a node without a picture, then the schema is wrong.

Where does Gatsby’s GraphQL schema come from? With Gatsby, we use plugins which fetch data from different sources. We then use that data to automatically infer a GraphQL schema.

How can we dictate a schema to GraphQL/Gatsby that always includes a picture, with 'null' as the default value if it's blank?

{
  allWordpressWpTestimonial {
    edges {
      node {
        id
        title
        acf {
          photo_fields {
            photo {
              id
              localFile {
                childImageSharp {
                  sizes {
                    src
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

In the example above, sometimes 'photo' doesn't exist and it breaks everything...

Gatsby config:

const innertext = require('innertext')
const url = require('url')

module.exports = {
  siteMetadata: {
    title: 'Test',
    googleMapsAPIKey: 'xxxxxx',
    adminBaseUrl: '123.123.123',
    adminProtocol: 'http',
  },
  pathPrefix: '/web/beta',
  plugins: [
    'gatsby-plugin-react-next',
    'gatsby-plugin-react-helmet',
    'gatsby-plugin-sharp',
    'gatsby-plugin-svgr',
    {
      resolve: 'gatsby-plugin-google-analytics',
      options: {
        trackingId: 'GOOGLE_ANALYTICS_TRACKING_ID',
      },
    },
    {
      resolve: 'gatsby-plugin-bugherd',
      options: {
        key: 'xxxxxx',
        showInProduction: true,
      },
    },
    {
      resolve: '@andrew-codes/gatsby-plugin-elasticlunr-search',
      options: {
        fields: ['title', 'url', 'textContent', 'urlSearchable'],
        resolvers: {
          wordpress__PAGE: {
            title: node => node.title,
            textContent: node => innertext(node.content),
            url: node => url.parse(node.link).path,
            urlSearchable: node =>
              url
                .parse(node.link)
                .path.split('/')
                .join(' '),
          },
          wordpress__POST: {
            title: node => node.title,
            textContent: node => innertext(node.content),
            url: node => `/news/${node.slug}`,
            urlSearchable: node =>
              url
                .parse(node.link)
                .path.split('/')
                .join(' '),
          },
          wordpress__wp_industry: {
            title: node => node.title,
            textContent: node => innertext(node.content),
            url: node => `/business/industries/${node.slug}`,
            urlSearchable: node =>
              url
                .parse(node.link)
                .path.split('/')
                .join(' '),
          },
        },
      },
    },
    {
      resolve: 'gatsby-source-wordpress',
      options: {
        baseUrl: 'xxxxxx',
        protocol: 'http',
        hostingWPCOM: false,
        useACF: true,
        auth: {
          htaccess_user: 'admin',
          htaccess_pass: 'xxxxxx',
          htaccess_sendImmediately: false,
        },
        verboseOutput: false,
      },
    },
    'gatsby-transformer-sharp',
  ],
}
like image 773
Ashbury Avatar asked Jun 09 '18 01:06

Ashbury


1 Answers

It's been awhile since this post, but since version 2.2 Gatsby has added a new API that'll make it much easier to customize schema. This is not an example with wordpress but with gatsby's gatsby-transformer-remark, but I'm sure it's applicable.

I have a bunch of .md with frontmatter looks like this:

---
title: "Screen title"
image: "./hero-image.png"  <--- sometimes it's an empty string, ""
category: "Cat"
---

...content...

If Gatsby get to the .md with the empty image first, it'll incorrectly infer that field as String, even though it should be File. With the new API, I can tell Gatsby about image field in gatsby-node.js:

exports.sourceNodes = ({ actions, schema }) => {
  const { createTypes } = actions
  createTypes(`
    type MarkdownRemarkFrontmatter {
      image: File
    }

    type MarkdownRemark implements Node {
      frontmatter: MarkdownRemarkFrontmatter
    }
  `)
}

This'll guarantee the image field to always be of File type, otherwise it'll be null.

Some notes:

  • Root nodes like MarkdownRemark has to implement Node
  • A node can implement multiple interface
  • You have to 'work your way' down to the relevant field. In this example, I have to declare the MarkdownRemarkFrontmatter type, then pass it to the frontmatter field in MarkdownRemark node.
  • Gatsby will infer the rest of the fields if not specified. In the example above, since I didn't specify the category field in MarkdownRemarkFrontmatter, it will be inferred by Gatsby just like before.
  • The most helpful way to find these types (MarkdownRemark, MarkdownRemarkFrontmatter) is to look for them in graphiql (default at localhost:8000/___graphql)
like image 148
Derek Nguyen Avatar answered Sep 18 '22 08:09

Derek Nguyen