Creating Custom Interfaces and Unions

One final common use case for schema customization in Gatsby involves creating custom interfaces and unions across multiple types through GraphQL’s abstract types. For instance, we could issue two queries for allAuthorJson and allTranslatorJson and then merge these by writing Gatsby code, but GraphQL can give us these types out of the box as a merged list.

Because both the AuthorJson and TranslatorJson types have most of their fields in common, we can create an interface that merges these two together:

// gatsby-node.js
exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes } = actions
  const typeDefs = `
    interface Creator {
      name: String!
      firstName: String!
      email: String!
    }
    type AuthorJson implements Node & Creator {
      name: String!
      firstName: String!
      email: String!
      created: Date
    }
    type TranslatorJson implements Node & Creator {
      name: String!
      firstName: String!
      email: String!
      multipleLanguages: Boolean
    }
  `
  createTypes(typeDefs)
}

In addition, we can implement the createResolvers API to facilitate a new type for us, allCreator, which makes our merged list available:

// gatsby-node.js
exports.createResolvers = ({ createResolvers }) => {
  const resolvers = {
    Query: {
      allCreator: {
        type: ["Creator"],
        resolve(source, args, context, info) {
          return context.nodeModel.getAllNodes({ type: "Creator" })
        },
      },
    },
  }
  createResolvers(resolvers)
}

Now, we can access the fields for both AuthorJson and TranslatorJson and acquire the authors’ and translators’ email addresses as follows:

export const query = graphql`
  {
    allCreator {
      ... on AuthorJson {
        email
      }
      ... on TranslatorJson {
        email
      }
    }
  }
`

As of Gatsby 3.0, it’s possible to use interface inheritance in schema customization to achieve the same result as seen in the preceding example. Using the implements keyword we can inherit from the Node interface, which will mean that the interface will behave like a normal top-level type that implements that interface, like allAuthor​J⁠son. This means we no longer need a resolver in Gatsby 3.0 implementations, because Gatsby will automatically add the requisite root query fields on our behalf:

// gatsby-node.js
exports.createSchemaCustomization = ({ actions }) => {
  const { createTypes } = actions
  const typeDefs = `
    interface Creator implements Node {
      id: ID!
      name: String!
      firstName: String!
      email: String!
    }

    type AuthorJson implements Node & Creator {
      name: String!
      firstName: String!
      email: String!
      created: Date
    }

    type TranslatorJson implements Node & Creator {
      name: String!
      firstName: String!
      email: String!
      multipleLanguages: Boolean
    }
  `
  createTypes(typeDefs)
}
WARNING

In Gatsby, every type that implements an interface that can be queried must also implement the Node interface.

This also means we only need to use fragments (discussed in Section 4) in the query for those fields that are not shared between both types:

export const query = graphql`
  {
    allCreator {
      nodes {
        name
        firstName
        email
        __typeName
        ... on AuthorJson {
          created
        }
        ... on TranslatorJson {
          multipleLanguages
        }
        ... on Node {
          parent {
            id
          }
        }
      }
    }
  }
`
NOTE

Schema customization in Gatsby also allows you to provide customizations that extend third-party GraphQL types that may have been supplied by remote sources, such as through the gatsby-source-graphql source plugin, by implementing the createResolvers API. For more information about this, consult the Gatsby documentation’s section on extending third-party types.


Posted

in

,

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *