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 allAuthorJson
. 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.
Leave a Reply