Reputation: 2356
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',
],
}
Upvotes: 16
Views: 8130
Reputation: 11577
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:
MarkdownRemark
has to implement Node
MarkdownRemarkFrontmatter
type, then pass it to the frontmatter
field in MarkdownRemark
node.category
field in MarkdownRemarkFrontmatter
, it will be inferred by Gatsby just like before.MarkdownRemark
, MarkdownRemarkFrontmatter
) is to look for them in graphiql (default at localhost:8000/___graphql
)Upvotes: 16
Reputation: 351
First are you using Gatsby-plugin-sharp, Gatsby-transform-sharp & Gatsby-source-WordPress Plugins ?
My site uses Gatsby-source-Wordpress Plugin plus the sharp library as well as Bluebird for returning promises etc. Define the ImageURL on your Post.js or Page.js. The Source URL is produced when loaded in my Media Library but is offloaded to a S3 bucket because my WordPress site is built "programmatically". The source URL is typically defined by you and can be chosen in ACF field types when build a post of page template.
export const pageQuery = graphql`
query homePageQuery {
site {
siteMetadata {
title
subtitle
description
}
}
allWordpressPost(sort: { fields: [date] }) {
edges {
node {
title
excerpt
slug
type
_image{
source_url
}
categories {
slug
name
}
}
}
}
}
Querying the data in the exact order is a must for each post type or GraphQL will not return the scheme correctly which will produce an error. As simple as it sounds and duplicative, there will have to be two different GraphQL schemes at times and two post.js example post1.js and post2.js files defining the different post categories. 1.Query for the return with Images URL. 2.Query for the return with no Images. to equal null or non-existant That is a downfall of GraphQL it expects to receive X and when Y happens it gets unhappy and fails.
You could also try this when you receive the image transform it with sharp to href= and transform it from https to size it on receiving.But in your case scheme it to be null. We did this for a employee bio page that was return from an old WordPress site.
/**
* Transform internal absolute link to relative.
*
* @param {string} string The HTML to run link replacemnt on
*/
linkReplace(string) {
// console.log(string)
const formatted = string.replace(
/(href="https?:\/\/dev-your-image-api\.pantheonsite\.io\/)/g,
`href="/`
)
return formatted
}
render() {
const post = { ...this.props.data.wordpressPost }
const headshot = { ...this.props.data.file.childImageSharp.resolutions }
const { percentScrolled } = { ...this.state }
const contentFormatted = this.linkReplace(post.content)
return (
<div ref={el => (this.post = el)}>
<div className={styles.progressBarWrapper}>
<div
style={{ width: `${percentScrolled}%` }}
className={styles.progressBar}
/>
</div>
<div className={styles.post}>
<h1
className={styles.title}
dangerouslySetInnerHTML={{ __html: post.title }}
/>
<div
className={styles.content}
dangerouslySetInnerHTML={{ __html: contentFormatted }}
/>
<Bio headshot={headshot} horizontal={true} />
</div>
</div>
)
}
}
Post.propTypes = {
data: PropTypes.object.isRequired,
}
export default Post
export const postQuery = graphql`
query currentPostQuery($id: String!) {
wordpressPost(id: { eq: $id }) {
wordpress_id
title
content
slug
}
file(relativePath: { eq: "your-image-headshot.jpg" }) {
childImageSharp {
resolutions(width: 300, height: 300) {
...GatsbyImageSharpResolutions
}
}
}
}
`
Hope this helps feel free to message me.
Upvotes: 0